最近很多小伙伴都在问深入理解Android中的Window和WindowManager和androidwindow详解这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展Android
最近很多小伙伴都在问深入理解Android中的Window和WindowManager和android window详解这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展Android Activity源码分析--windowmanager屏幕旋转研究、Android EditText 自动拼写检查报错:android.view.WindowManag、Android OS 3.2上的WindowManagerImpl中的NullPointerException、Android WindowManager 悬浮窗:不需要申请权限实现悬浮等相关知识,下面开始了哦!
本文目录一览:- 深入理解Android中的Window和WindowManager(android window详解)
- Android Activity源码分析--windowmanager屏幕旋转研究
- Android EditText 自动拼写检查报错:android.view.WindowManag
- Android OS 3.2上的WindowManagerImpl中的NullPointerException
- Android WindowManager 悬浮窗:不需要申请权限实现悬浮
深入理解Android中的Window和WindowManager(android window详解)
Window表示一个窗口的概念,Window是一个抽象类,它的具体实现是PhoneWindow。创建一个Window,需要通过WindowManager即可完成,WindowManager是外界访问Window的入口,Window具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC的过程。Android中,所有的视图都是通过Window来呈现,不管是Activity、Dialog、还是Toast,它们的视图实际上都是附加在Window上,因此Window是实际View的直接管理者,单击事件由Window传递给DecorView,然后再由DecorView传递给我们的View,就连Activity的设置视图方法setContentView在底层也是通过Window来完成的。
Window和WindowManager
添加一个Window的过程,重点代码是:
mWindowManager.addView(mFLoatingButton,mLayoutParams);
WindowManager.LayoutParams中有两个flags和type参数。
Flags参数有三个Window属性
- FLAG_NOT_FOCUSABLE。表示Window不需要获取焦点,也不需要接收各种输入事件,最终事件会直接传递给下层的具有焦点的Window
- FLAG_NOT_TOUCH_MODAL。在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理,这个标记很重要,一般来说都需要开启此标记,否则其他Window将无法收到单击事件
- FLAG_SHOW_WHEN_LOCKED。开启此模式可以让Window显示在锁屏的界面上。
Type参数表示Window的类型,有三种类型,分别是应用Window,子Window和系统Window,应用类Window对应一个Activity,子Window不能单独存在,它需要附属在特定的父Window之中,比如常见的Dialog就是一个子Window,系统Window是需要声明权限在能创建的Window,比如Toast和系统状态栏这些都是系统Window。
Window是分层的,每个Window都有对应的z-ordered,层级大的会覆盖在层级小的Window的上面,在三类Window中,应用类的Window的层级范围是1-99,子Window的层级范围是1000-1999,系统Window的层级的范围是2000-2999,这些层级范围对应着WindowManager.LayoutParams的Type参数。如想要Window位于所有Window的最顶层,那么采用较大的层级即可。很显然系统Window层级是最大的,而且系统层级有很多值。
WindowManager所提供的功能很简单,常用有三个方法,即添加View,更新View和删除View,这三个方法定义在ViewManager中,而WindowManager继承了ViewManager。
Window的内部机制
Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,说明View才是Window存在的实体,在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。
Window的添加过程
Window的添加过程需要通过WindowManager的addView来实现,WindowManager是一个接口,它的真正实现是WindowManagerImpl类。
@Override public void addView(View view,ViewGroup.LayoutParams params){ mGlobal.addView(view,params,mdisplay,mParentwindow); } @Override public void updateViewLayout(View view,ViewGroup.LayoutParams params){ mGlobal.updateViewLayout(view,params); } @Override public void removeView(View view){ mGlobal.removeView(view,false); }
可以看到,WindowManagerImpl并没有直接实现Window的三大操作,而是全部交给了WindowManagerGlobal来处理,WindowManagerGlobal以工厂的形式向外提供自己的实例。WindowManagerGlobal的addView方法主要分为如下几步:
- 检查参数是否合法,如果是子Window那么还需要调整一些布局参数
- 创建ViewRootImpl并将View添加到列表中
- 通过ViewRootImpl来更新界面并完成Window的添加过程
Window的删除过程
Window的删除过程和添加过程一样,都是先通过WindowManagerImpl后,在进一步通WindowManagerGlobal来实现的。里面用到一个dispatchDetachedFromWindow方法内部实现,这个方法主要做了四件事:
垃圾回收相关的工作,比如清除数据和消息、移除回调
通过Session的remove方法删除Window
调用View的dispatchDetachedFromWindow方法,在内部会调用View的onDetachedFromWindow()以及onDetachedFromWindowInternal()
调用WindowManagerGlobal的doRemoveView方法刷新数据
Window的更新过程
主要是用到updateViewLayout方法,首先它需要更新View的LayoutParams并替换掉老的LayoutParams,接着再更新ViewRootImpl中的LayoutParams,这一步是通过ViewRootImpl的setLayoutParams方法来实现的。在ViewRootImpl中会通过scheduleTraversals方法对View进行重新布局,包括测量、布局、重绘这三个过程。
Window的创建过程
View是Android中的视图呈现方式,但是View不能单独存在,它必须附着在Window这个抽象的概念上面,因此有视图的地方就有Window。
Activity的Window创建过程
如何创建,需要了解Activity启动过程,比较复杂,但它最终由ActivityThred中的perfromLaunchActivity()来完成整个启动过程,在这个方法内部会通过类加载器创建Activity的实例对象,并调用其attach方法为其关联运行过程中所依赖的一系列上下文环境变量。
在Activity的attach方法里,系统会创建Activity所属的Window对象并为其设置回调接口,Window对象的创建是通过PolicyManager的makeNewWindow方法实现的,对于Activity的setContentView的实现可以看出,Activity将具体实现交给了Window处理,而Window的具体实现是PhoneWindow,所以只需要看PhoneWindow相关逻辑即可,大致以下几个步骤:
- 如果没有DecorView,那么就创建它。DecorView是一个FrameLayout,是Activity的顶级View,一般来说它的内部包含标题栏和内部栏。
- 将View添加到DecorView的mContentParent中。
- 回调Activity的onContentChanged方法通知Activity视图已经发生改变。Activity的onContentChanged是一个空实现。
经过上面三个步骤,DecorView已经被创建初始化完毕,Activity的布局文件已经成功添加到了DecorView的mContentParent中,但是这个时候DecorView还没有被WindowManager正式添加到Window中,真正被视图调用是在Activity的onResume方法,接着会调用Activity的makeVisible(),正是在makeVisible方法中,DecorView真正地完成了添加和显示这两个过程。
Dialog的Window创建过程
Dialog的Window的创建过程和Activity类似,有以下几个步骤:
- 创建Window。同样是通过PolicyManager的makeNewWindow方法来完成的。
- 初始化DecorView并将Dialog的视图添加到DecorView中。
- 将DecorView添加到Window中并显示。在Dialog的show方法中,会通过WindowManager将DecorView添加到Window中。
普通的Dialog有一个特殊之处,那就是必须采用Activity的Context,如果采用Application的Context,就会报错。
Toast的Window创建过程
Toast和Dialog不同,它的工作过程稍微复杂。首先Toast也是基于Window来实现的,但是由于Toast具有定时取消这一功能,所以系统采用了Handler。在Toast的内部有两类的IPC过程,第一类是Toast访问notificationmanagerService,第二类是notificationmanagerService回调Toast的TN接口。
Toast属于系统Window,它内部的视图有两种方式指定,一种是系统默认的样式,另一种是通过setView方法来指定一个自定义view,不管如何,它们都对应Toast的一个View类型的内部成员mNextView。Toast提供了show和cancel分别用于显示和隐藏Toast,它们的内部是一个IPC过程。
以上所述是小编给大家介绍的Android中的Window和WindowManager,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程小技巧网站的支持!
Android Activity源码分析--windowmanager屏幕旋转研究
注意:鄙人看的是6.0的代码
Activity里面还是调用了WindowManager来显示界面。在activity的738行,有这几行代码
private Window mWindow;
private WindowManager mWindowManager;
/*package*/ View mDecor = null; //这就是activity的主view,我也不知道怎么表达会比较好 = =
平常用的setContentView方法,最终是调用了mWindow来处理的。
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
mWindow其实一个 PhoneWindow对象,在6169行的attach方法可是看到它的初始化
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);//我在这里
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
}
其中mWindowManager是通过mWindow来实现初始化
mWindowManager = mWindow.getWindowManager();
接着看看activity是怎么调用mWindowManager来显示界面,在源码第4772行,可以看到一个setVisible方法,这就是activity展示界面的入口吧
public void setVisible(boolean visible) {
if (mVisibleFromClient != visible) {
mVisibleFromClient = visible;
if (mVisibleFromServer) {
if (visible) makeVisible();//我将要展示界面
else mDecor.setVisibility(View.INVISIBLE);
}
}
}
接着看看makeVisible方法是怎么调用mWindowManager来显示界面。没错,其实就是用addview来显示界面。
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
为什么activity能通过android:configChanges="orientation|keyboardHidden" ,能保证界面不重建呢,我们可以看到源码还有一个onWindowAttributesChanged方法。
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
// Update window manager if: we have a view, that view is
// attached to its parent (which will be a RootView), and
// this activity is not embedded.
if (mParent == null) {
View decor = mDecor;
if (decor != null && decor.getParent() != null) {
getWindowManager().updateViewLayout(decor, params);
}
}
}
我估计是用了updateViewLayout来刷新页面,单纯看到这里,activity是怎么监听屏幕旋转呢?
Android EditText 自动拼写检查报错:android.view.WindowManag
需要给EditText设置inputtype属性,将自动拼写检查屏蔽掉.
android:inputType="textNoSuggestions"
Android OS 3.2上的WindowManagerImpl中的NullPointerException
例外是:
FATAL EXCEPTION: main java.lang.NullPointerException at android.view.WindowManagerImpl.reportNewConfiguration(WindowManagerImpl.java:427) at android.app.ActivityThread.handleUpdatePackageCompatibilityInfo(ActivityThread.java:2801) at android.app.ActivityThread.access$2700(ActivityThread.java:122) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1151) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:132) at android.app.ActivityThread.main(ActivityThread.java:4123) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:491) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) at dalvik.system.NativeStart.main(Native Method)
我的测试代码:
TestActivity.java(由Wizerd生成)
package com.test; import android.app.Activity; public class TestActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Intent intent = new Intent(this,TestService.class); startService(intent); } }
TestService.java(大多数函数都是空的)
Package com.test; import android.content.ComponentName; public class TestService extends Service { private boolean m_connected = false; private ServiceConnection m_connInitService = new ServiceConnection() { public void onServiceConnection(ComponentName className,IBinder service) { m_connected = true; } public void onServicedisconnected(ComponentName className) { } }; public static class TestServiceBinder extends Binder { } public IBinder onBind(Intent intent) { return new TestServiceBinder(); } public void onDestroy() { super.onDestroy(); } public int onStartCommand(Intent intent,int flags,int startId) { return 1; } }
如果我在同一过程中没有服务或服务运行测试应用程序,屏幕兼容性开关将不会导致任何问题.但是,我不明白为什么服务会在屏幕兼容性切换期间导致系统异常.这是因为服务流程是非UI流程吗?哪个可能会触发Android核心代码中的错误?
解决方法
android:process
If the name assigned to this attribute begins with a colon (‘:’),a
new process,private to the application,is created when it’s needed
and the service runs in that process. If the process name begins with
a lowercase character,the service will run in a global process of
that name,provided that it has permission to do so. This allows
components in different applications to share a process,reducing
resource usage.
因此,如果您指定以小写字母开头的进程名称,则应具有执行此操作的权限.但我不知道检查这些权限的位置.也许它们在WindowManagerImpl中被检查,它找不到这个全局进程,因此返回null.这只是一个假设.
Android WindowManager 悬浮窗:不需要申请权限实现悬浮
Android WindowManager 悬浮窗:不需要申请权限实现悬浮
附录文章 1 介绍了 Android 平台上的悬浮窗 WindowManager,WindowManager 悬浮窗可以悬浮在 Android 设备上的桌面窗口之上,但是 WindowManager 的使用,必须先申请权限,在一些定制的 Android 操作系统中,有可能会将 WindowManager 悬浮窗的权限一律屏蔽掉,这就导致基于 WindowManager 的 APP 功能难以实现。
然而,可以变通的通过设置 WindowManager 的类型,通过设置 WindowManager 的类型为 WindowManager.LayoutParams.TYPE_TOAST,从而不需要申请 WindowManager 悬浮窗权限就可以使用 WindowManager。
写一个例子。一个简单的不需要任何布局的 main activity:
package zhangphil.demo;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
TextView textView = new TextView(this);
textView.setGravity(Gravity.CENTER);
textView.setBackgroundColor(Color.BLACK);
textView.setText("zhang phil @ csdn");
textView.setTextSize(10);
textView.setTextColor(Color.RED);
//类型是TYPE_TOAST,像一个普通的Android Toast一样。这样就不需要申请悬浮窗权限了。
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_TOAST);
//初始化后不首先获得窗口焦点。不妨碍设备上其他部件的点击、触摸事件。
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = 300;
//params.gravity=Gravity.BOTTOM;
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplication(), "不需要权限的悬浮窗实现", Toast.LENGTH_LONG).show();
}
});
WindowManager windowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
windowManager.addView(textView, params);
}
}
代码运行结果:
通过这样的策略实现 WindowManager,可以做很多事情,比如 Android 常见的音乐类 APP 的底部播放条,可以考虑使用这种手段实现。通过 WindowManager 做一个音乐播放条放在 APP 的顶部长期驻留。
附录:
1,《Android WindowManager 悬浮窗》链接:http://blog.csdn.net/zhangphil/article/details/50382966
2,《Android 桌面小部件 AppWidget(1)》链接:http://blog.csdn.net/zhangphil/article/details/50457355
3,《Android 桌面小部件 AppWidget(2)》链接:http://blog.csdn.net/zhangphil/article/details/50461944
4,《Android 桌面小部件 AppWidget:音乐播放器桌面控制部件 Widget(3)》链接:http://blog.csdn.net/zhangphil/article/details/50466844
今天关于深入理解Android中的Window和WindowManager和android window详解的讲解已经结束,谢谢您的阅读,如果想了解更多关于Android Activity源码分析--windowmanager屏幕旋转研究、Android EditText 自动拼写检查报错:android.view.WindowManag、Android OS 3.2上的WindowManagerImpl中的NullPointerException、Android WindowManager 悬浮窗:不需要申请权限实现悬浮的相关知识,请在本站搜索。
本文标签: