赞
踩
JS在服务器端,可以随时更新。给用户的APP预留一些常用工具类,通过JS 调用android 反射生成对象,进而调用android预留的工具类方法。比如各种uitl
js代码:
function invoke(){
JSTest.webapp_invoke("com.android.test2mvvm.Test2_App","test");
JSTest.webapp_invoke("com.android.test2mvvm.Test2_App","test","good");
JSTest.webapp_invoke("com.android.test2mvvm.Test2_App","test","good",123456);
JSTest.webapp_invoke("com.android.test2mvvm.Test2_App","test","false",123456);
}
被调用android端代码:
binding.Webview.addJavascriptInterface(new WebApp() { @JavascriptInterface public void showToast(String msg) { Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show(); } @JavascriptInterface public void webapp_invoke(String class_name, String method_name, String string) { super.webapp_invoke(class_name, method_name, super.string_to_object(string)); } @JavascriptInterface public void webapp_invoke(String class_name, String method_name, String string, String string1) { Object[] objects = new Object[]{super.string_to_object(string), super.string_to_object(string1)}; Loge.e(string+":"+string1); super.webapp_invoke(class_name, method_name, objects); } @JavascriptInterface public void webapp_invoke(String class_name, String method_name) { Loge.e("测试" + ":" + method_name + ":" + class_name); super.webapp_invoke(class_name, method_name); } }, "JSTest");
因为JS调用android端 传过来的都是String,所以需要转换一下,android 的转换 代码如下:
因为不是做项目,只是demo测试,只简单的转换了boolean和int,string类型的,其他的大同小异,根据需要补上即可
public Object string_to_object(String string) {
if (string.equals("true")) {
return true;
}
if (string.equals("false")) {
return false;
}
try {
Integer integer= Integer.valueOf(string);
return integer; //能转换成功,没有Exception 则说明是int,否则为string
} catch (Exception exception) {
return string;
}
}
根据JS传过来的String 反射生成对象,并调用方法的WebApp类:
public class WebApp { public void test() { } public void test(String string) { } public void app_invoke(Object object, Method method, Object... args) { try { method.invoke(object, args); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } public Object app_getObject(String class_name) { try { Class clazz = Class.forName(class_name); Method method = clazz.getDeclaredMethod("getInstance", null);//这里 直接根据getInstance 获取到单例 然后用来调用对象的各个方法 Loge.e(class_name); Object object = method.invoke(null); return object; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } public Method app_getMethod(String class_name, String method_name, Object... objects) { try { Class clazz = Class.forName(class_name); Class[] classes1 = new Class[objects.length]; for (int i = 0; i < objects.length; i++) { Class c = objects[i].getClass(); classes1[i] = c; Loge.e(c.getName()); } Method method = clazz.getMethod(method_name, classes1); return method; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return null; } public Object string_to_object(String string) { if (string.equals("true")) { return true; } if (string.equals("false")) { return false; } try { Integer integer= Integer.valueOf(string); return integer; } catch (Exception exception) { return string; } } public void webapp_invoke(String class_name, String method_name, Object... objects) { app_invoke(app_getObject(class_name), app_getMethod(class_name, method_name, objects), objects); } }
最终被调用的Test2_App类:
@HiltAndroidApp public class Test2_App extends Application { public static Application context; public static Application getInstance() { //尽量每一个工具类都用 getInstance()来获取单例,这样在WebApp反射的时候,可以拿到单例,进而可以调用里面的方法. return context; } public void test(String s) { Loge.e("测试一下" + s); } public void test() { Loge.e("测试一下"); } public void test(String s, Integer jj) { Loge.e("测试一下" + s + "-" + jj); } public void test(Boolean b, Integer integer) { Loge.e(String.valueOf(b) + integer + "测试测试"); } }
服务端随时可以更改用户的UI,随时可以增加或者删除一些功能,不可能强求用户三天俩头更新升级的,一些灵活多变的UI尽量用JS实现,只需每次用户登录的时候更新一下即可。而且 android+h5是大势所趋。在后面追加一个使用room的实例,,,使用JS的好处就是灵活多变:
function getUser(){ JSTest.webapp_invoke("com.android.test2mvvm.test5.fragment7.dao.AppDatabase","getAllUser");//获取所有用户 } var json={"account":"4","introduction":"null","nickname":"null","pwd":"4","uid":4}; //定义一个用户 的JSON var jsonStr = JSON.stringify(json); //直接传JSON 传不过去,需要转义一下 // JS调用安卓的方法,并且传递的参数为json格式的字符串(JSONObject.toString()), // // 例如: var json = {"name":"XJY","age":25",company":"CSII"}; // //直接将json作为参数传递:window.name.jsToClient(json); // // Android获取的参数是不可用的,打印出来的是undefinded。 // // JS要这样处理,再作为参数传递给原生: // // var jsonStr = JSON.stringify(json); // // window.name.jsToClient(jsonStr); // // 这样Android才能接受到json的字符串。 function updateUser(){//更新用户 JSTest.webapp_invoke("com.android.test2mvvm.test5.fragment7.dao.AppDatabase","updateUser",jsonStr); } function insertUser(){//插入用户 JSTest.webapp_invoke("com.android.test2mvvm.test5.fragment7.dao.AppDatabase","insertUser",jsonStr); }
下面这是User的 bean:
public class User extends BaseObservable {
@PrimaryKey
private int uid;
private String account;
private String pwd;
@Ignore
private String confirmPwd;
private String nickname;
private String introduction;
private String avatar;
.........//都是一键生成的代码
}
这是AppDatabase数据库类:
@Database(entities = {User.class}, version = 1, exportSchema = false) public abstract class AppDatabase extends RoomDatabase { private static final String DATABASE_NAME = "mvvm_demo.db"; private static volatile AppDatabase mInstance; /** * 单例模式 */ public static AppDatabase getInstance(Context context) { if (mInstance == null) { synchronized (AppDatabase.class) { if (mInstance == null) { mInstance = Room.databaseBuilder(context, AppDatabase.class, DATABASE_NAME).build(); } } } return mInstance; } public static AppDatabase getInstance() { return mInstance; } public abstract UserDao userDao(); public void getAllUser() { //提供给JS调用 Loge.e(userDao().getAll_01().toString()); } public void updateUser(String jsonuser) {//提供给JS调用 Loge.e(jsonuser+"------------"); Gson gson = new Gson(); User user = gson.fromJson(jsonuser, User.class); Loge.e(user.toString()); userDao().update(user); } public void insertUser(String jsonuser){//提供给JS调用 Loge.e(jsonuser+"------------"); Gson gson = new Gson(); User user = gson.fromJson(jsonuser, User.class); Loge.e(user.toString()); userDao().insert(user); } }
下面是DAO,操作room数据库 UserDao类:
@Dao public interface UserDao { @Query("SELECT * FROM user") LiveData<List<User>> getAll(); @Query("SELECT * FROM user") List<User> getAll_01(); @Query("select * from user where uid = 1") LiveData<User> getUser01(); @Update void update(User user); @Insert(onConflict = OnConflictStrategy.REPLACE) void insert(User user); @Query("DELETE FROM user") void deleteAll(); }
就这样,就完成了 在JS调用android原生的类 代码 操作。
来个效果截图:
上面实现的功能随时增删改查,用户的UI可以每天多变,一点进去再下载新界面,一更新,又是一个新界面了。
在android 原生代码 尽量把工具类的常用功能实现。随时提供给JS 调用即可。JS方根据实际情况需求调用
当然,涉及到安全问题,在下载界面的时候加密即可,
关于使用Webview 内存泄露问题,我个人建议是,所有有使用WebView 的地方,统一放到另外一个activity 另开一个进程,退出activity 的时候,直接给它来个
@Override
protected void onDestroy() {
super.onDestroy();
Loge.e("退出");
System.exit(0);//直接退出进程,一刀切了,干净省事。什么内存泄露,什么内存不够,等等玩意,都是不存在的。
}
activity另开进程代码:
<activity
android:name=".test6.Test6_Activity"
android:process=":web" />
就这么解决了webview 的痛点难点了。主进程和webview 的私有进程 互不干扰,重要数据用腾讯的 MMKV传过去即可,实时数据,AADL一个,,,进程之间一般也不会有多大的数据传递,不至于会影响用户体验。主进程尽量简便轻巧,主要是负责其他进程的入口,没事就挂着,也不会卡顿。负责push的进程 就学某多多,弄一个其他公司的logo,不让用户发觉,时不时的偷偷来广告。用户要怨恨,就怨恨其他公司去,哈哈,跟我某多多无关。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。