Preserving the state of an Android WebView on screen orientation change

本文介绍了一种在Android中使用WebView时保持屏幕旋转前后状态的方法,通过避免重新创建WebView实例来确保页面滚动位置、缩放级别等状态得到保留。

Copied from http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/

 

Preserving the state of an Android WebView on screen orientation change

Source code

If you’ve tried to use a WebView inside your app, you know that the standard behavior on screen orientation change is not satisfactory in most cases because the full state of the WebView is not preserved. Here I’m going to show you a possible implementation to keep the full state of theWebView every time you rotate the screen. This is the same implementation I used in my own app (FWebLauncher) for the internal web browser.

Standard implementation (the state is not completely preserved)

Let’s start by taking a look at what a standard implementation would look like. This is how you would usually implement the state saving for a WebView according to the Android documentation:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class StandardImplActivity extends Activity
{
   protected WebView webView;
 
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
     super .onCreate(savedInstanceState);
     setContentView(R.layout.standard_impl);
 
     // Retrieve UI elements
     webView = ((WebView)findViewById(R.id.webView));
     
     // Initialize the WebView
     webView.getSettings().setSupportZoom( true );
     webView.getSettings().setBuiltInZoomControls( true );
     webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
     webView.setScrollbarFadingEnabled( true );
     webView.getSettings().setLoadsImagesAutomatically( true );
 
     // Load the URLs inside the WebView, not in the external web browser
     webView.setWebViewClient( new WebViewClient());
 
     if (savedInstanceState == null )
     {
       // Load a page
       webView.loadUrl( "http://www.google.com" );
     }
   }
   
   @Override
   protected void onSaveInstanceState(Bundle outState)
   {
     super .onSaveInstanceState(outState);
 
     // Save the state of the WebView
     webView.saveState(outState);
   }
   
   @Override
   protected void onRestoreInstanceState(Bundle savedInstanceState)
   {
     super .onRestoreInstanceState(savedInstanceState);
 
     // Restore the state of the WebView
     webView.restoreState(savedInstanceState);
   }
}

So we call the saveState and the restoreState methods in the onSaveInstanceState and theonRestoreInstanceState methods of the Activity.

The WebView is declared directly in the layout file for the activity:

?
01
02
03
04
05
06
07
08
09
10
<? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   android:layout_width = "fill_parent"
   android:layout_height = "fill_parent" >
   
   < WebView android:id = "@+id/webView"
     android:layout_width = "fill_parent"
     android:layout_height = "fill_parent" />
   
</ LinearLayout >

The main problem with this implementation is that, whenever you rotate the screen, the WebViewis created again because the activity is destroyed and its saveState method doesn’t save the full state, but only a part of it like the URL of the page that was loaded and the browsing history. So it happens that for example the zoom and the scroll position are not preserved after the screen orientation change and sometimes the page is reloaded from the web.

State preserving implementation (a possible solution)

I tried many different solutions to preserve the full state of the WebView on screen rotation and the following one proved to be a reliable one that solves our problem. The main point is that the activity must not be destroyed and we must handle the screen orientation change by ourselves. Let’s start by taking a look at the layout file:

?
01
02
03
04
05
06
07
08
09
10
<? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   android:layout_width = "fill_parent"
   android:layout_height = "fill_parent" >
   
   < FrameLayout android:id = "@+id/webViewPlaceholder"
     android:layout_width = "fill_parent"
     android:layout_height = "fill_parent" />
   
</ LinearLayout >

You immediately notice the difference with the standard implementation. Here we don’t declare the WebView inside the layout file, but we declare a placeholder instead. It is the position where our WebView will be placed inside the activity.

Also the code inside the activity will be different of course and here it is:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class StatePreservingImplActivity extends Activity
{
   protected FrameLayout webViewPlaceholder;
   protected WebView webView;
 
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
     super .onCreate(savedInstanceState);
     setContentView(R.layout.state_preserving_impl);
     
     // Initialize the UI
     initUI();
   }
   
   protected void initUI()
   {
     // Retrieve UI elements
     webViewPlaceholder = ((FrameLayout)findViewById(R.id.webViewPlaceholder));
 
     // Initialize the WebView if necessary
     if (webView == null )
     {
       // Create the webview
       webView = new WebView( this );
       webView.setLayoutParams( new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
       webView.getSettings().setSupportZoom( true );
       webView.getSettings().setBuiltInZoomControls( true );
       webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
       webView.setScrollbarFadingEnabled( true );
       webView.getSettings().setLoadsImagesAutomatically( true );
 
       // Load the URLs inside the WebView, not in the external web browser
       webView.setWebViewClient( new WebViewClient());
 
       // Load a page
       webView.loadUrl( "http://www.google.com" );
     }
 
     // Attach the WebView to its placeholder
     webViewPlaceholder.addView(webView);
   }
 
   @Override
   public void onConfigurationChanged(Configuration newConfig)
   {
     if (webView != null )
     {
       // Remove the WebView from the old placeholder
       webViewPlaceholder.removeView(webView);
     }
 
     super .onConfigurationChanged(newConfig);
     
     // Load the layout resource for the new configuration
     setContentView(R.layout.state_preserving_impl);
 
     // Reinitialize the UI
     initUI();
   }
   
   @Override
   protected void onSaveInstanceState(Bundle outState)
   {
     super .onSaveInstanceState(outState);
 
     // Save the state of the WebView
     webView.saveState(outState);
   }
   
   @Override
   protected void onRestoreInstanceState(Bundle savedInstanceState)
   {
     super .onRestoreInstanceState(savedInstanceState);
 
     // Restore the state of the WebView
     webView.restoreState(savedInstanceState);
   }
}

We need to change also the AndroidManifest.xml file to intercept the configuration changes inside the activity:

?
1
2
3
4
< activity
   android:name = ".StatePreservingImplActivity"
   android:configChanges = "keyboard|keyboardHidden|orientation"
   android:label = "State preserving implementation" />

In the android:configChanges attribute we are telling the system that we don’t want the activity to be restarted when the declared configuration changes happen, but we want to handle the configuration changes by ourselves through the onConfigurationChanged method that will be called every time there is a change.

When the activity is created we initialize its content with the current layout and we initialize the user interface with the initUI method. That’s where the WebView is created, stored in thewebView class field and then attached to the webViewPlaceholder element filling its space completely. Every time the screen is rotated, the onConfigurationChanged method is called so we remove the WebView from its current placeholder first, then we load the new activity layout (we can define a different layout for the portrait and the landscape configuration) and reinitialize the UI. After a configuration change, the activity instance is still the same because it has not been destroyed. The initUI method retrieves the new placeholder instance while the WebView instance is still the same because the webView class field is not changed, so the WebView is not created again and all we have to do is attach the old WebView to the new webViewPlaceholder instance.

This implementation is very useful in case you defined a different layout depending on the screen orientation. For example, you could have a my_activity_layout.xml file in the res/layout folder of your Android project for the portrait orientation and another my_activity_layout.xml file in theres/layout-land folder with a different content for the landscape orientation. WhenonConfigurationChanged is called, the system already has the appropriate resources for the new configuration and when you call the setContentView method passing it a layout resource id, the system uses the correct layout file for the current screen orientation, you just have to make sure that the ids of the UI elements are the same in both files and that the webViewPlaceholderelement is present in both the layout configurations. This is what I have also in the internal web browser of my FWebLauncher app and that allows me to have a different position and layout of the buttons bar depending on the screen orientation while keeping the same WebView instance.

I’m sure you noticed that the onSaveInstanceState and onRestoreInstanceState methods are still there, but why since the activity is not destroyed anymore? Well, they are there in case the activity is destroyed for a reason that is not a configuration change (e.g. the system needs to destroy the activity because it is not in foreground and there’s a need to free some memory). In that case we can still restore the original state of the WebView, just without preserving the complete state because we’re going to lose the zoom and the scroll position for example, anyway it’s still better than totally losing the WebView state.

If you want to read more about the configuration change handling in Android, you can check thespecific page of the Android documentation. You can also download an example application through the link on top of this post to take a look at the source code that shows what I described in the post.

 

内容概要:本文详细介绍了一个基于Java和Vue的联邦学习隐私保护推荐系统的设计与实现。系统采用联邦学习架构,使用户数据在本地完成模型训练,仅上传加密后的模型参数或梯度,通过中心服务器进行联邦平均聚合,从而实现数据隐私保护与协同建模的双重目标。项目涵盖完整的系统架构设计,包括本地模型训练、中心参数聚合、安全通信、前后端解耦、推荐算法插件化等模块,并结合差分隐私与同态加密等技术强化安全性。同时,系统通过Vue前端实现用户行为采集与个性化推荐展示,Java后端支撑高并发服务与日志处理,形成“本地训练—参数上传—全局聚合—模型下发—个性化微调”的完整闭环。文中还提供了关键模块的代码示例,如特征提取、模型聚合、加密上传等,增强了项目的可实施性与工程参考价值。 适合人群:具备一定Java和Vue开发基础,熟悉Spring Boot、RESTful API、分布式系统或机器学习相关技术,从事推荐系统、隐私计算或全栈开发方向的研发人员。 使用场景及目标:①学习联邦学习在推荐系统中的工程落地方法;②掌握隐私保护机制(如加密传输、差分隐私)与模型聚合技术的集成;③构建高安全、可扩展的分布式推荐系统原型;④实现前后端协同的个性化推荐闭环系统。 阅读建议:建议结合代码示例深入理解联邦学习流程,重点关注本地训练与全局聚合的协同逻辑,同时可基于项目架构进行算法替换与功能扩展,适用于科研验证与工业级系统原型开发。
源码来自:https://pan.quark.cn/s/a4b39357ea24 遗传算法 - 简书 遗传算法的理论是根据达尔文进化论而设计出来的算法: 人类是朝着好的方向(最优解)进化,进化过程中,会自动选择优良基因,淘汰劣等基因。 遗传算法(英语:genetic algorithm (GA) )是计算数学中用于解决最佳化的搜索算法,是进化算法的一种。 进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择、杂交等。 搜索算法的共同特征为: 首先组成一组候选解 依据某些适应性条件测算这些候选解的适应度 根据适应度保留某些候选解,放弃其他候选解 对保留的候选解进行某些操作,生成新的候选解 遗传算法流程 遗传算法的一般步骤 my_fitness函数 评估每条染色体所对应个体的适应度 升序排列适应度评估值,选出 前 parent_number 个 个体作为 待选 parent 种群(适应度函数的值越小越好) 从 待选 parent 种群 中随机选择 2 个个体作为父方和母方。 抽取父母双方的染色体,进行交叉,产生 2 个子代。 (交叉概率) 对子代(parent + 生成的 child)的染色体进行变异。 (变异概率) 重复3,4,5步骤,直到新种群(parentnumber + childnumber)的产生。 循环以上步骤直至找到满意的解。 名词解释 交叉概率:两个个体进行交配的概率。 例如,交配概率为0.8,则80%的“夫妻”会生育后代。 变异概率:所有的基因中发生变异的占总体的比例。 GA函数 适应度函数 适应度函数由解决的问题决定。 举一个平方和的例子。 简单的平方和问题 求函数的最小值,其中每个变量的取值区间都是 [-1, ...
处理器在接收到意外中断后进入无限循环,通常表明系统在中断处理过程中出现了逻辑错误或硬件异常未能正确响应。此类问题的解决需要从软件和硬件两个层面进行排查与优化。 ### 中断处理中的常见问题 当处理器接收到一个未被预期的中断时,可能由于以下原因导致进入无限循环: 1. **中断未被正确清除**:某些外设在触发中断后,必须通过特定寄存器操作来清除中断标志位,否则该中断将不断被重新触发[^3]。 2. **中断服务例程(ISR)逻辑错误**:如果 ISR 没有正确处理中断源,或者在处理过程中发生了死循环,则会导致 CPU 无法正常返回主程序。 3. **中断优先级配置不当**:高优先级中断持续抢占低优先级中断,可能导致某些中断永远无法完成处理,进而引发系统卡顿或循环。 4. **堆栈溢出或现场保护失败**:在进入中断时,CPU 需要保存当前执行状态(即“保护现场”)。如果堆栈空间不足或堆栈指针设置错误,可能导致现场保存失败,从而进入不可预测的状态[^2]。 5. **非法中断向量地址**:如果中断向量表配置错误,处理器可能会跳转到无效地址执行代码,若此处无有效指令则可能陷入死循环。 ### 解决方案 #### 1. 确保中断标志正确清除 在编写中断服务程序时,应确保及时清除中断标志位。例如,在使用 STM32 微控制器的 GPIO 外部中断时,需调用 `EXTI_ClearITPendingBit()` 函数来清除中断标志: ```c void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) { // 处理中断逻辑 ... // 清除中断标志 EXTI_ClearITPendingBit(EXTI_Line0); } } ``` #### 2. 添加中断处理调试信息 在 ISR 中添加调试输出(如点亮 LED 或串口打印),以确认是否进入了中断以及是否成功退出: ```c void USART1_IRQHandler(void) { // 调试:点亮 LED 表示进入中断 GPIO_SetBits(GPIOC, GPIO_Pin_13); // 处理中断逻辑 ... // 调试:熄灭 LED 表示退出中断 GPIO_ResetBits(GPIOC, GPIO_Pin_13); } ``` #### 3. 检查中断向量表配置 确保中断向量表指向正确的中断处理函数。对于 ARM Cortex-M 系列处理器,可以通过如下方式设置向量表偏移: ```c SCB->VTOR = (uint32_t)0x08004000; // 设置新的向量表地址 ``` 同时检查链接脚本中是否已正确分配向量表空间。 #### 4. 启用看门狗机制 为防止因中断处理异常导致系统完全冻结,可以启用硬件看门狗定时器(WDT),一旦系统进入死循环而未能及时“喂狗”,看门狗将强制复位系统: ```c // 初始化看门狗 IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); IWDG_SetPrescaler(IWDG_Prescaler_64); IWDG_SetReload(0xFFF); IWDG_Enable(); // 在主循环中定期喂狗 while (1) { IWDG_ReloadCounter(); ... } ``` #### 5. 使用调试器分析中断行为 借助调试器(如 J-Link、ST-Link)设置断点,观察中断发生时 PC 是否跳转至正确地址,查看堆栈是否被正确压栈,以及中断返回指令是否被执行。 #### 6. 增加默认中断处理函数 为所有未使用的中断提供默认处理函数,并在其中加入错误日志记录或系统重启机制,以便捕获非法中断事件: ```c void Default_Handler(void) { // 记录错误信息 printf("Unexpected interrupt occurred!\n"); // 可选:系统重启 NVIC_SystemReset(); } ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值