Android WebView应用被系统杀死后,如何维护/重建数据库连接?
我开发了一个内嵌HTML、CSS、JS的WebView Android应用,配备多个本地数据库存储数据,该应用每次需连续使用10-11小时。
目前发现一个严重问题:应用进入后台一段时间后会被Android系统杀死,导致所有任务需重新执行。我需要维护或重建Android与数据库的连接,以确保应用稳定运行。
我想咨询两个问题:
- 是否有办法维护Android与数据库的连接(例如使用前台服务),使应用被杀死后不影响连接?
- 应用被系统杀死后,能否让Android与JS重建连接(例如进行数据库同步)?
MainActivity代码
public class MainActivity extends AppCompatActivity { HTMLDataBase myDB1; ContentTableDataBase myDB2; CloudTableDataBase myDB3; InterviewDataBase myDB4; Context context; boolean check; private WebView view; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myDB1 = new HTMLDataBase(this); myDB2 = new ContentTableDataBase(this); myDB3 = new CloudTableDataBase(this); myDB4 = new InterviewDataBase(this); display_html(); Javascript_Android_Communication(); } @Override protected void onResume() { super.onResume(); myDB1 = new HTMLDataBase(this); myDB2 = new ContentTableDataBase(this); myDB3 = new CloudTableDataBase(this); myDB4 = new InterviewDataBase(this); display_html(); Javascript_Android_Communication(); } @Override protected void onStop() { super.onStop(); this.finish(); } public void display_html() { WebView view = (WebView) findViewById(R.id.WebView); view.getSettings().setJavaScriptEnabled(true); view.getSettings().setDomStorageEnabled(true); view.loadUrl("file:///android_asset/index.html"); } public void Javascript_Android_Communication() { WebView view = (WebView) findViewById(R.id.WebView); view.setWebChromeClient(new WebChromeClient() {}); WebSettings settings = view.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); view.addJavascriptInterface(new JavaScriptInterface(this), "Android"); view.addJavascriptInterface(new WebViewJavaScriptInterface(this), "app"); } }
WebViewJavaScriptInterface代码
public class WebViewJavaScriptInterface { HTMLDataBase myDB1; ContentTableDataBase myDB2; CloudTableDataBase myDB3; InterviewDataBase myDB4; Context mContext; WebViewJavaScriptInterface (Context c) { mContext = c; } @JavascriptInterface public void Javascript_to_Android(String data_id, String html_element, String state) { myDB1 = new HTMLDataBase((MainActivity) mContext); myDB1.updateData(data_id, html_element, state); } @JavascriptInterface public void Javascript_to_Content_Table_DataBase(String data_id, String duty_time, String my_duty, String checkbox) { myDB2 = new ContentTableDataBase((MainActivity) mContext); myDB2.insertData(data_id, duty_time, my_duty, checkbox); } @JavascriptInterface public void add_cloud_data(String data_id, String Time, String Total_Cloud, String Low_Medium_Cloud, String FirstCloud, String SecondCloud, String ThirdCloud, String ForthCloud, String FifthCloud, String SixthCloud, String SeventhCloud, String EighthCloud, String Visibility) { myDB3 = new CloudTableDataBase((MainActivity) mContext); boolean response = myDB3.insertData(data_id, Time, Total_Cloud, Low_Medium_Cloud, FirstCloud, SecondCloud, ThirdCloud, ForthCloud, FifthCloud, SixthCloud, SeventhCloud, EighthCloud, Visibility); if (response == true) { Toast.makeText(mContext, "Data inserted", Toast.LENGTH_SHORT).show(); } if (response == false) { Toast.makeText(mContext, "Data not inserted", Toast.LENGTH_SHORT).show(); } } @JavascriptInterface public void update_cloud_data(String data_id, String Time, String Total_Cloud, String Low_Medium_Cloud, String FirstCloud, String SecondCloud, String ThirdCloud, String ForthCloud, String FifthCloud, String SixthCloud, String SeventhCloud, String EighthCloud, String Visibility) { myDB3 = new CloudTableDataBase((MainActivity) mContext); boolean response = myDB3.updateData(data_id, Time, Total_Cloud, Low_Medium_Cloud, FirstCloud, SecondCloud, ThirdCloud, ForthCloud, FifthCloud, SixthCloud, SeventhCloud, EighthCloud, Visibility); if (response == true) { Toast.makeText(mContext, "Data updated", Toast.LENGTH_SHORT).show(); } if (response == false) { Toast.makeText(mContext, "Data not updated", Toast.LENGTH_SHORT).show(); } } @JavascriptInterface public void delete_duty(String id) { myDB2.deleteData(id); } @JavascriptInterface public void delete_cloud(String id) { myDB3.deleteData(id); } }
针对问题1:维护数据库连接&降低应用被杀死概率
首先明确:应用进程被系统完全杀死后,所有进程内资源(包括数据库连接)都会被释放,无法维持连接。但可以通过以下方式优化,降低被杀死的概率,并在重启后快速恢复数据库操作:
使用前台服务
前台服务会在状态栏显示通知,系统优先级更高,不容易被回收。可以将核心数据操作(如定时同步、批量持久化)放在前台服务中,但注意不要长期持有数据库连接——SQLite自带连接池管理,每次操作后关闭连接反而更稳定,避免内存泄漏。
实现要点:创建继承Service的类,在onStartCommand中调用startForeground传入通知ID和通知实例,将服务置于前台状态。优化数据库连接管理
你的代码中每次JS调用都新建数据库实例,会频繁打开/关闭连接,建议用单例模式复用数据库实例:// 示例:HTMLDataBase单例实现 public class HTMLDataBase extends SQLiteOpenHelper { private static HTMLDataBase instance; public static synchronized HTMLDataBase getInstance(Context context) { if (instance == null) { instance = new HTMLDataBase(context.getApplicationContext()); } return instance; } private HTMLDataBase(Context context) { super(context, "html_db", null, 1); } }后续所有数据库操作都通过
getInstance获取实例,减少连接开销。移除不必要的Activity销毁逻辑
你在onStop中调用this.finish(),会导致应用进入后台时直接销毁Activity,大幅提升被系统回收的概率。建议移除该行代码,让Activity正常进入后台栈:@Override protected void onStop() { super.onStop(); // 移除 this.finish(); }
针对问题2:应用被杀死后重建Android与JS的连接
应用被完全杀死后,WebView和JS环境都会被销毁,无法直接重建之前的连接,但可以通过以下方式实现数据同步和状态恢复:
启动时主动同步数据
在MainActivity中,监听WebView页面加载完成事件,主动调用JS方法同步本地数据库数据:view.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // 将本地数据库数据转为JSON,调用JS方法同步 view.evaluateJavascript("syncAndroidData(" + getDatabaseDataAsJson() + ")", null); } });需实现
getDatabaseDataAsJson()方法,把数据库数据转换成JSON格式传递给JS,让JS恢复之前的操作状态。用WorkManager实现后台同步
如果需要在应用被杀死后仍能进行数据库同步(如与云端同步),可以使用WorkManager创建周期性任务。WorkManager的任务会持久化,即使设备重启也能继续执行,适合长时间后台同步需求。保存关键状态到本地
在Activity的onSaveInstanceState中保存关键状态(如当前操作的任务ID、未完成的数据),重启时从Bundle中恢复并传递给JS:private String currentTaskId; @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("currentTaskId", currentTaskId); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); currentTaskId = savedInstanceState.getString("currentTaskId"); // 恢复后传递给JS view.evaluateJavascript("restoreTaskState('" + currentTaskId + "')", null); }
复用WebView实例
你的display_html和Javascript_Android_Communication方法每次都重新获取WebView实例,建议在onCreate中初始化一次并保存为成员变量,避免重复查找和配置:private WebView view; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); view = findViewById(R.id.WebView); setupWebViewSettings(); myDB1 = HTMLDataBase.getInstance(this); myDB2 = ContentTableDataBase.getInstance(this); myDB3 = CloudTableDataBase.getInstance(this); myDB4 = InterviewDataBase.getInstance(this); view.loadUrl("file:///android_asset/index.html"); setupJavascriptInterfaces(); } private void setupWebViewSettings() { WebSettings settings = view.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); view.setWebChromeClient(new WebChromeClient() {}); } private void setupJavascriptInterfaces() { view.addJavascriptInterface(new JavaScriptInterface(this), "Android"); view.addJavascriptInterface(new WebViewJavaScriptInterface(this), "app"); }优化数据库操作
在WebViewJavaScriptInterface中,将新建数据库实例改为使用单例:@JavascriptInterface public void Javascript_to_Android(String data_id, String html_element, String state) { HTMLDataBase myDB1 = HTMLDataBase.getInstance(mContext); myDB1.updateData(data_id, html_element, state); }
内容的提问来源于stack exchange,提问作者Lo Dennis

