【0171】Android 面试-基本知识
1.课程基本讲解




2.activity面试问题

2.1 Activity的生命周期
2.1.1 Activity的四种状态

【running状态】Activity是处于活动状态的,用户可以点击屏幕,然后屏幕可以做出响应;此时的Activity处于栈顶;
【paused状态】Activity失去焦点,或者是被一个非全屏的Activity占据,或者是一个透明的Activity占据;
此时的状态是Activity只是不可以和用户进行交互,但是可以内存的对象和变量是保存的,除非在内存紧张的时候会将此Activity销毁;
【stoped状态】不可见,但是在后台,内存的对象和变量是保存的,除非在内存紧张的时候会将此Activity销毁;
【killed状态】Activity被彻底销毁,内存的对象和变量已经不存在了;
2.1.2 Activity的生命周期


2.1.3 Android 进程优先级

【前台】可以与用户进行交互;
【可见】用户可见但是不可以交互;
【服务】在后台开启的service服务;
【后台】前台的进程点击home键之后所处的进程就是后台进程,除非在内存紧张的时候会将此进程资源被回收;
【空进程】数据的暂存,优先级最低,随时可能被回收;
2.2 android 任务栈

2.3 android 的启动模式
【说明】详见参见《Android 开发艺术探索》





2.4 scheme跳转协议

scheme是一种页面内跳转协议,主要用于支持一下几种场景:
服务器下发跳转路径,客户端根据服务器下发跳转路径跳转相应的页面;
H5页面点击锚点,根据锚点具体跳转路径App端跳转具体的页面;
App端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面
下面我将简单介绍一下scheme的基本概念以及以上三种场景下scheme的具体应用。
URL scheme 概述
URL scheme 的作用
客户端应用可以向操作系统注册一个 URL scheme,该 scheme 用于从浏览器或其他应用中启动本应用。
通过指定的 URL 字段,可以让应用在被调起后直接打开某些特定页面,比如车辆详情页、订单详情页、消息通知页、促销广告页等等。
也可以执行某些指定动作,如订单支付等。
也可以在应用内通过 html 页来直接调用显示 app 内的某个页面。
URL scheme 的格式
客户端自定义的 URL 作为从一个应用调用另一个的基础,遵循 RFC 1808 (Relative Uniform Resource Locators) 标准。这跟我们常见的网页内容 URL 格式一样。
一个普通的 URL 分为几个部分,scheme、host、relativePath、query。
比如:http://www.baidu.com/s?rsv_bp=1&rsv_spt=1&wd=NSurl&inputT=2709,
这个URL中,scheme 为 http,host 为 www.baidu.com,relativePath 为 /s,query 为 rsv_bp=1&rsv_spt=1&wd=NSurl&inputT=2709。
一个应用中使用的 URL 例子(该 URL 会调起车辆详情页):
uumobile://mobile/carDetail?car_id=123456,其中 scheme 为 uumobile,host 为 mobile,relativePath 为 /carDetail,query 为 car_id=123456。
Scheme定义Activity
1)在Androidmanifest.xml中定义scheme
<!-- scheme协议 -->
<activity
Android:name=".UI.translate.NativeAppActivity"
Android:label="@string/app_name">
<!-- 要想在别的App上能成功调起App,必须添加intent过滤器 -->
<intent-filter>
<!-- 协议部分,随便设置 -->
<data Android:scheme="uumobile" />
<!-- 下面这几行也必须得设置 -->
<category Android:name="Android.intent.category.DEFAULT" />
<category Android:name="Android.intent.category.BROWSABLE" />
<action Android:name="Android.intent.action.VIEW" />
</intent-filter>
</activity>
这样我们便定义了能够接受scheme请求的activity实例,当网页或者是Android代码发送这种规则scheme的请求的时候就能够调用NativeAppActivity了。
2)当然就是实现NativeAppActivity
/**
* Created by admin
*/
public class NativeAppActivity extends Activity{
public String tag = "NativeAppActivity";
public Activity mContext = null;
public void onCreate(Bundle b)
{
super.onCreate(b);
mContext = this;
Uri uri = getIntent().getData();
if (uri != null)
{
List<String> pathSegments = uri.getPathSegments();
String uriQuery = uri.getQuery();
Intent intent;
if (pathSegments != null && pathSegments.size() > 0) {
// 解析SCHEME
if (someif) {
dosomething();
}
else {
// 若解析不到SCHEME,则关闭NativeAppActivity;
finish();
}
} else {
finish();
}
} else {
finish();
}
}
}
NativeAppActivity这个类中主要用于实现对scheme的解析,然后做出相应的动作,比如请求scheme跳转登录页面,我们可以这样定义
uumobile://appname/gotoLogin
然后我们解析出scheme如果是这样的结构就跳转登录页面。。。
这里简单说一下,我们可以通过Intent对象获取调用的scheme的host等信息
this.getIntent().getScheme();//获得Scheme名称
this.getIntent().getDataString();//获得Uri全部路径
3)通过服务器下发跳转路径跳转相应页面
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("uumobile://yongche/123123123")));
这里的”uumobile://yongche/123123123”就是服务器下发的跳转路径,当我们执行startActivity的时候就会调起NativeAppActivity,然后我们通过在NativeAppActivity解析scheme的内容,跳转相应的页面。
4)通过在H5页面的锚点跳转相应的页面
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//解析scheme
if (url.indexOf(H5Constant.SCHEME) != -1) {
try {
Uri uri = Uri.parse(url);
String[] urlSplit = url.split("\\?");
Map<String, String> queryMap = new HashMap<String, String>();
String h5Url = null;
if (urlSplit.length == 2) {
queryMap = H5Constant.parseUriQuery(urlSplit[1]);
h5Url = queryMap.get(H5Constant.MURL);
}
// 跳转NativeAppActivity解析
{
// 若设置刷新,则刷新页面
if (queryMap.containsKey(H5Constant.RELOADPRE) && "1".equals(queryMap.get(H5Constant.RELOADPRE))) {
h5Fragment.isNeedFlushPreH5 = true;
}
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
h5Activity.startActivityForResult(intent, H5Constant.h5RequestCode);
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
// 打电话
else if (url.indexOf("tel://") != -1) {
final String number = url.substring("tel://".length());
Config.callPhoneByNumber(h5Activity, number);
return true;
} else if (url.indexOf("tel:") != -1) {
final String number = url.substring("tel:".length());
Config.callPhoneByNumber(h5Activity, number);
return true;
}
// 其他跳转方式
else {
view.loadUrl(url);
//如果不需要其他对点击链接事件的处理返回true,否则返回false
return false;
}
}
可以发现我们为Webview设置了WebViewClient,并重写了WebViewClient的shouldOverrideUrlLoading方法,然后我们解析锚点的url,
并根据解析的内容调起NativeAppActivity的scheme Activity,然后在NativeAppActivity中解析scheme的内容并跳转相应的页面。
5)根据服务器下发通知栏消息,App跳转相应的页面
public class NotificationActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
L.i("接收到通知点击事件...");
Intent realIntent = getIntent().getParcelableExtra(NotifeConstant.REAL_INTENT);
// 解析scheme并跳转
gotoRealScheme(this, realIntent);
}
/**
* notification中跳转SCHEME,根据有效时间判断跳转URL地址
* 跳转之后更具网络请求判断用户当前状态
*/
private void gotoRealScheme(Context context, Intent realIntent) {
if (realIntent == null || context == null) {
finish();
return;
}
try {
L.i("开始解析通知中的参数...");
long startShowTime = realIntent.getLongExtra(NotifeConstant.START_SHOW_TIME, 0);
// 有效期时间,单位:s(秒)
long validTime = realIntent.getLongExtra(NotifeConstant.VALID_TIME, 0);
long currentTime = System.currentTimeMillis();
String validActionUrl = realIntent.getStringExtra(NotifeConstant.VALID_ACTION_URL);
String invalidActionUrl = realIntent.getStringExtra(NotifeConstant.INVALID_ACTION_URL);
Intent schemeIntent;
L.i("开始根据URL构建Intent对象...");
if ((currentTime - startShowTime) / 1000L <= validTime) {
schemeIntent = H5Constant.buildSchemeFromUrl(validActionUrl);
} else {
schemeIntent = H5Constant.buildSchemeFromUrl(invalidActionUrl);
}
if (schemeIntent != null) {
// 设置当前页面为通知栏打开
Config.isNotificationOpen = true;
context.startActivity(schemeIntent);
finish();
//对通知栏点击事件统计
MobclickAgent.onEvent(context, UMCountConstant.PUSH_NOTIFICATION_CLICK);
} else {
finish();
}
} catch (Exception e) {
// 异常情况下退出当前Activity
finish();
}
}
}
服务器下发的所有的通知都是先跳转这里的NotificationActivity,然后在这里执行跳转其他Activity的逻辑,而这里的H5Constant的buildSchemeFromUrl方法就是构造跳转页面Intent对象的,
我们可以看一buildSchemeFromUrl方法的具体实现:
/**
* 从scheme的url中构建出Intent,用于界面跳转
*
* @param url
* @return
*/
public static Intent buildSchemeFromUrl(String url) {
if (url != null && url.indexOf(H5Constant.SCHEME) != -1) {
Uri uri = Uri.parse(url);
String[] urlSplit = url.split("\\?");
Map<String, String> queryMap = new HashMap<String, String>();
String h5Url = null;
if (urlSplit.length == 2) {
queryMap = H5Constant.parseUriQuery(urlSplit[1]);
h5Url = queryMap.get(H5Constant.MURL);
}
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
if (!TextUtils.isEmpty(h5Url)) {
intent.putExtra(H5Constant.MURL, h5Url);
}
return intent;
}
return null;
}
这样我们就搞构造除了跳转NativeAppActivity的Intent对象,并将scheme字符串传递给了NativeAppActivity,这样在NativeAppActivity中就可以解析scheme字符串并执行相应的跳转逻辑了。
总结:
Android中的scheme是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面;
通过scheme协议,服务器可以定制化告诉App跳转那个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转页面等。
3.Fragment 讲解
3.1 Fragment 的认识
【说明】在使用的当中,fragment的使用也比较多,fragment更节省内存,切换的效果更好,具有自己的生命周期;
但是:fragment非对立,需要嵌在Acitivity中使用;
3.2 fragment加载到Activity的两种方式-静态加载和动态加载

【常用的是动态加载】

3.3 FragmentPagerAdapter和FragmentStatePagerAdapter的区别
【说明】前者使用到页面较少的时候,后者使用到页面较多的时候;
在页面切换的时候,FragmentPagerAdapter只是将页面分离,没有将销毁内存中的数据;
FragmentStatePagerAdapter在页面切换的时候,会将页面的数据内存中销毁;



3.4 fragment的生命周期

上面只是展示了 Fragment 与 Activity 生命周期最基本的关系,如果通过 addToBackStack() 将 Fragment 放入回退栈,
然后通过 popBackStack() 出栈,Fragment 的生命周期会如何变化呢?
如果 Fragment 与 ViewPager 结合使用,Fragment 的生命周期又是如何?如果通过 hide() 和 show() 方法来展示隐藏,这时 Fragment 的生命周期
本文将对Android开发中的Activity&Fragment生命周期进行全面解析
Fragment的生命周期
先来看张官方说明图
详细解读每个方法的调用场景
- onAttach方法
Fragment和Activity建立关联的时候调用(获得activity的传递的值) - onCreateView方法
为Fragment创建视图(加载布局)时调用(给当前的fragment绘制UI布局,可以使用线程更新UI) - onActivityCreated方法
当Activity中的onCreate方法执行完后调用(表示activity执行oncreate方法完成了的时候会调用此方法) - onDestroyView方法
Fragment中的布局被移除时调用(表示fragment销毁相关联的UI布局) - onDetach方法
Fragment和Activity解除关联的时候调用(脱离activity)
fragment生命周期解析
-
当一个fragment被创建的时候:
onAttach()
onCreate()
onCreateView()
onActivityCreated() -
当这个fragment对用户可见的时候,它会经历以下状态。
onStart()
onResume() -
当这个fragment进入“后台模式”的时候,它会经历以下状态。
onPause()
onStop() -
当这个fragment被销毁了(或者持有它的activity被销毁了):
onPause()
onStop()
onDestroyView()
onDestroy()
onDetach() -
就像Activity一样,在以下的状态中,可以使用Bundle对象保存一个fragment的对象。
onCreate()
onCreateView()
onActivityCreated()
其他场景的调用
-
屏幕灭掉 /回到桌面
onPause() onSaveInstanceState() onStop() -
屏幕解锁 /回到应用
onStart() onResume() -
切换到其他Fragment
onPause() onStop() onDestroyView() -
切换回本身的Fragment
onCreateView() onActivityCreated() onStart() onResume() -
退出应用
onPause() onStop() onDestroyView() onDestroy() onDetach()
Fragment和Activity的生命周期很相似,以下是对比图
3.5 fragment的通信

3.6 fragment的增加删除替换

一直想总结一下Fragment与Fragment、Activity通信的问题,今天有时间一共总结了三种,权当抛砖引玉
如果大家还有更好的方式来实现Fragment和Fragment、Activity的通信,欢迎提出来,我们一起学习。好了,我们先来看看今天要实现的一个效果图:
左边是一个Fragment,右边是一个Fragment,当我们点击左边的Fragment的时候,右边的Fragment中显示出我们所点击的人的作品,我们就来看看怎样实现两个Fragment之间的通信。
1.直接在一个Fragment中调用另外一个Fragment中的方法
我们可以直接在一个Fragment中调用另外一个Fragment的公开方法,前提是要先拿到另外一个Fragment的实例,我们先来看看怎样在左边的Fragment中拿到右边Fragment的实例:
- ContentFragment cf = (ContentFragment) getActivity()
- .getFragmentManager().findFragmentById(
- R.id.content_fg);
- cf.showPro(name);
我们通过宿主Activity拿到FragmentManager,进而再拿到右边的Fragment,然后调用右边Fragment里边的showPro方法,其中要传入的参数是左边点击的人名,我们看看右边Fragment中的showPro方法:
- public void showPro(String key) {
- list = map.get(key);
- adapter = new ArrayAdapter<String>(getActivity(),
- android.R.layout.simple_list_item_1, list);
- lv.setAdapter(adapter);
- }
先根据传进来的人名拿到该人作品的数据集,然后构造一个adapter赋值给listview,很简单吧。
使用这种方式我们可以直接在一个Fragment中调用另一个Fragment的公开方法,从而实现两个Fragment的通信。
这种方法适于那些我们在布局文件中就已经定义了的Fragment,这种Fragment每个都有id,可以通过FragmentManager找到,但是如果我们使用了ViewPager,即每个Fragment都是动态添加进来的,
这个时候我们无法通过FragmentManager获得另外一个Fragment的实例,那么该怎么办呢?这时我们就要用到下面这种方式了。
- public interface showPro {
- public void showProByName(String name);
- }
- private showPro mCallback;
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- if (activity != null) {
- mCallback = (showPro) activity;
- }
- }
- public class MainActivity extends Activity implements showPro {
- private ContentFragment cf;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- getActionBar().hide();
- cf = (ContentFragment) getFragmentManager().findFragmentById(
- R.id.content_fg);
- }
- @Override
- public void showProByName(String name) {
- cf.showPro(name);
- }
- }
public void showPro(String key) {
list = map.get(key); //此处使用的hashMap存储的数据;
adapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, list);
lv.setAdapter(adapter);
}
- lv.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view,
- int position, long id) {
- TextView nameTV = (TextView) view;
- String name = nameTV.getText().toString();
- if ("韩愈".equals(name)) {
- mCallback.showProByName(name);
- }
- }
- });
- Intent intent = new Intent("showPro");
- intent.putExtra("name", name);
- LocalBroadcastManager.getInstance(getActivity())
- .sendBroadcast(intent);
在右边Fragment中接收广播:
- LocalBroadcastManager localBroadcastManager = LocalBroadcastManager
- .getInstance(getActivity());
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction("showPro");
- BroadcastReceiver br = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String key = intent.getStringExtra("name");
- list = map.get(key);
- adapter = new ArrayAdapter<String>(getActivity(),
- android.R.layout.simple_list_item_1, list);
- lv.setAdapter(adapter);
- }
- };
- localBroadcastManager.registerReceiver(br, intentFilter);
1 package com.lenve.fragment; 2 3 import android.app.Activity; 4 import android.app.Fragment; 5 import android.content.Intent; 6 import android.os.Bundle; 7 import android.support.v4.content.LocalBroadcastManager; 8 import android.view.LayoutInflater; 9 import android.view.View; 10 import android.view.ViewGroup; 11 import android.widget.AdapterView; 12 import android.widget.AdapterView.OnItemClickListener; 13 import android.widget.ArrayAdapter; 14 import android.widget.ListView; 15 import android.widget.TextView; 16 17 import java.util.ArrayList; 18 import java.util.List; 19 20 public class NameFragment extends Fragment { 21 22 private List<String> names; 23 private ListView lv; 24 private showPro mCallback; 25 private ArrayAdapter<String> adapter; 26 27 public interface showPro { 28 public void showProByName(String name); 29 } 30 31 @Override 32 public void onCreate(Bundle savedInstanceState) { 33 super.onCreate(savedInstanceState); 34 initData(); 35 } 36 37 private void initData() { 38 names = new ArrayList<String>(); 39 names.add("韩愈"); 40 names.add("柳宗元"); 41 names.add("苏轼"); 42 names.add("苏辙"); 43 names.add("苏洵"); 44 names.add("欧阳修"); 45 names.add("曾巩"); 46 names.add("王安石"); 47 } 48 49 @Override 50 public View onCreateView(LayoutInflater inflater, ViewGroup container, 51 Bundle savedInstanceState) { 52 View v = inflater.inflate(R.layout.name_fg, null); 53 initLv(v); 54 return v; 55 } 56 57 private void initLv(View v) { 58 lv = (ListView) v.findViewById(R.id.name_lv); 59 adapter = new ArrayAdapter<String>(getActivity(), 60 android.R.layout.simple_list_item_1, names); 61 lv.setAdapter(adapter); 62 lv.setOnItemClickListener(new OnItemClickListener() { 63 64 @Override 65 public void onItemClick(AdapterView<?> parent, View view, 66 int position, long id) { 67 TextView nameTV = (TextView) view; 68 String name = nameTV.getText().toString(); 69 70 if ("韩愈".equals(name)) { 71 72 mCallback.showProByName(name); 73 74 } else if ("柳宗元".equals(name)) { 75 ContentFragment cf = (ContentFragment) getActivity() 76 .getFragmentManager().findFragmentById( 77 R.id.content_fg); 78 cf.showPro(name); 79 80 } else if ("苏轼".equals(name) || "苏辙".equals(name)) { 81 Intent intent = new Intent("showPro"); 82 intent.putExtra("name", name); 83 LocalBroadcastManager.getInstance(getActivity()) 84 .sendBroadcast(intent); 85 86 } else if ("苏洵".equals(name)) { 87 ((MainActivity) getActivity()).showProByName(name); 88 } 89 } 90 }); 91 } 92 93 @Override 94 public void onAttach(Activity activity) { 95 super.onAttach(activity); 96 if (activity != null) { 97 mCallback = (showPro) activity; 98 } 99 } 100 101 public void clearData() { 102 names = new ArrayList<String>(); 103 adapter = new ArrayAdapter<String>(getActivity(), 104 android.R.layout.simple_list_item_1, names); 105 lv.setAdapter(adapter); 106 } 107 }
【源码】com.lenve.fragment.ContentFragment
1 package com.lenve.fragment; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 8 import android.app.Fragment; 9 import android.content.BroadcastReceiver; 10 import android.content.Context; 11 import android.content.Intent; 12 import android.content.IntentFilter; 13 import android.os.Bundle; 14 import android.support.v4.content.LocalBroadcastManager; 15 import android.view.LayoutInflater; 16 import android.view.View; 17 import android.view.ViewGroup; 18 import android.widget.ArrayAdapter; 19 import android.widget.ListView; 20 21 public class ContentFragment extends Fragment { 22 23 private Map<String, List<String>> map; 24 private List<String> list; 25 private ListView lv; 26 private ArrayAdapter<String> adapter; 27 28 @Override 29 public void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 initData(); 32 initBroadcast(); 33 } 34 35 @Override 36 public View onCreateView(LayoutInflater inflater, ViewGroup container, 37 Bundle savedInstanceState) { 38 View v = inflater.inflate(R.layout.content_fg, null); 39 initLv(v); 40 return v; 41 } 42 43 private void initLv(View v) { 44 lv = (ListView) v.findViewById(R.id.content_lv); 45 list = new ArrayList<String>(); 46 adapter = new ArrayAdapter<String>(getActivity(), 47 android.R.layout.simple_list_item_1, list); 48 lv.setAdapter(adapter); 49 } 50 51 private void initData() { 52 map = new HashMap<String, List<String>>(); 53 list = new ArrayList<String>(); 54 list.add("师说"); 55 list.add("马说"); 56 list.add("早春呈水部张十八员外"); 57 map.put("韩愈", list); 58 list = new ArrayList<String>(); 59 list.add("小石潭记"); 60 list.add("江雪"); 61 list.add("捕蛇者说"); 62 list.add("小石城山记"); 63 map.put("柳宗元", list); 64 list = new ArrayList<String>(); 65 list.add("水调歌头"); 66 list.add("念奴娇·赤壁怀古"); 67 list.add("江城子·密州出猎"); 68 list.add("赤壁赋"); 69 list.add("题西林壁"); 70 map.put("苏轼", list); 71 list = new ArrayList<String>(); 72 list.add("黄州快哉亭记"); 73 list.add("上枢密韩太尉书"); 74 map.put("苏辙", list); 75 list = new ArrayList<String>(); 76 list.add("六国论"); 77 list.add("九日和韩魏公"); 78 list.add("心术"); 79 list.add("管仲论"); 80 map.put("苏洵", list); 81 } 82 83 public void showPro(String key) { 84 list = map.get(key); 85 adapter = new ArrayAdapter<String>(getActivity(), 86 android.R.layout.simple_list_item_1, list); 87 lv.setAdapter(adapter); 88 } 89 90 private void initBroadcast() { 91 LocalBroadcastManager localBroadcastManager = LocalBroadcastManager 92 .getInstance(getActivity()); 93 IntentFilter intentFilter = new IntentFilter(); 94 intentFilter.addAction("showPro"); 95 BroadcastReceiver br = new BroadcastReceiver() { 96 97 @Override 98 public void onReceive(Context context, Intent intent) { 99 String key = intent.getStringExtra("name"); 100 list = map.get(key); 101 adapter = new ArrayAdapter<String>(getActivity(), 102 android.R.layout.simple_list_item_1, list); 103 lv.setAdapter(adapter); 104 } 105 106 }; 107 localBroadcastManager.registerReceiver(br, intentFilter); 108 } 109 }
1 package com.lenve.fragment; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.view.View; 6 7 import com.lenve.fragment.NameFragment.showPro; 8 9 public class MainActivity extends Activity implements showPro { 10 11 private ContentFragment cf; 12 private NameFragment nf; 13 14 @Override 15 protected void onCreate(Bundle savedInstanceState) { 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.activity_main); 18 getActionBar().hide(); 19 cf = (ContentFragment) getFragmentManager().findFragmentById( 20 R.id.content_fg); 21 nf = (NameFragment) getFragmentManager().findFragmentById(R.id.name_fg); 22 } 23 24 @Override 25 public void showProByName(String name) { 26 cf.showPro(name); 27 } 28 29 public void onClick(View v) { 30 switch (v.getId()) { 31 case R.id.clear_data: 32 nf.clearData(); 33 break; 34 35 default: 36 break; 37 } 38 } 39 }
4.Fragment直接调用Activity中的public方法
我们也可以直接在Fragment中调用Activity中的公开方法,如下:- ((MainActivity) getActivity()).showProByName(name);
3.3 使用Bundle
但是还可以用另外一种方法,就是用官方提供的Bundler去实现Activity传递数据到Fragment. Activity(传递数据): public class MainActivity extends Activity { private FragmentManager mManager; private Button mBtnPutData; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBtnPutData = (Button) findViewById(R.id.btn_putData); //默认显示的Fragment mManager= getFragmentManager(); FragmentTransaction transaction = mManager.beginTransaction(); RightFragment fragment = new RightFragment(); transaction.replace(R.id.fl_content, fragment); transaction.commit(); //向RightFragment传值 mBtnPutData.setOnClickListener(new OnClickListener() { public void onClick(View v) { FragmentTransaction transaction = mManager.beginTransaction(); RightFragment fragment = new RightFragment(); //创建Bundle对象 Bundle bundle = new Bundle(); //向Bundle输入数据 bundle.putInt("data", 100); //对fragment设置Bundle参数 fragment.setArguments(bundle); transaction.replace(R.id.fl_content, fragment); transaction.commit(); } }); } } Fragment(接收数据) public class RightFragment extends Fragment{ private EditText mEtData; public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { View inflate = inflater.inflate(R.layout.right_fragment, container,false); mEtData = (EditText) inflate.findViewById(R.id.et_data); //获取Activity传递的Bundle对象 Bundle bundle = getArguments(); if(bundle != null){ //从bundle里面获取key为data的int类型数值 int data = bundle.getInt("data"); //将值设置到控件上 et_data.setText("data="+data); } return inflate; } }
4.Service
4.1 Service是什么?
【说明】
【1】service可以被Activity/broadcast等等启动,一旦启动,就会一直在后台运行;即使启动的控件已经销毁,但是service仍然存在;
【2】service可以与Activity进行绑定,然后进行数据交互;
【3】service与Activity是在不同的进程中,可以跨进程通信;
【4】service和broadcast都运行在主线程,不能长时间的做耗时的操作;---必须注意;

4.2 service和Thread的区别
【说明】
【1】service运行是Android特有的,依托于主线程进行操作,非独立;
而Thread是程序执行的最小单元,线程、可以执行一些异步的操作,可以自己独立进行运行的;
【2】service和Thread没有任何的关联;service运行在主线程,不能做耗时的操作;Thread是独立的子线程,可以独立的运行;两者是完全不同的;
【3】证明service和主线程是运行在同一个线程中的;



【4】 service 和Thread不能联系在一起;服务和后台也是两个不同概念;
【5】service运行做耗时的操作,也需要一定要开启单独的子线程;
【问】为什么在service中创建子线程操作,而不在Activity中创建子线程?
【非常非常重要】Activity很难对子线程Thread进行控制;特别是当Activity被销毁的时候,无法对子线程进行控制;
【1】因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。
【2】而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。
【3】但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。
因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。
【说明】service创建后台任务是安全的,service可以完全对后台进行控制;
【6】耗时的操作需要放到Thread中;service:长时间在后台运行,不需要与UI交互,可以使用service;
【service的作用】: (1).它用于处理一些不干扰用户使用的后台操作。如下载,网络获取。播放音乐,他可以通过INTENT来开启,同时也可以绑定到宿主对象(调用者例如ACTIVITY上)来使用。 (2).如果说Activity是显示前台页面的信息,那么Service就是在后台进行操作的。如果Service和前台UI进行交互的话可以通过发送广播或者通知栏的方式。 【启动方式】: (1).Service自己不能运行,需要通过某一个Activity或者其它Context对象来调用。 (2).Service的启动方式有两种: Context.startService()和Context.bindService()两种方式启动Service。
如果在service的onCreate()方法或者onStart()方法中有耗时的操作,要新开启一个线程。
在需要service的地方通过以上两种方式来启动。 注意: 平常使用多的是startService方法,可以把一些耗时的任务放到后台去处理,当处理完成后,可以通过广播或者通知栏来通知前台。
4.3 Service创建的两种方式?
【说明】通过Activity启动service,即使Activity销毁了,但是service是仍然可以在后台运行的;
4.3.1 startIntent方式




4.3.2 Bindservice方法
【说明】
【1】多个Activity可以绑定一个Service,
【2】提供了客户端和服务端的接口,允许activity和service进行数据的交互和请求的发送等等;
【3】如果Activity处于不同的进程中时,可以完成进程间的通信;
【4】所有的操作要在Acitivity绑定到service之后才能运行;
【5】如果activity销毁,则service自动销毁,不需要手动调用stopservice;

【说明】客户端指的是activity,服务端指的是service;




【转载】
第一种方式:通过StartService启动Service
通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。
要创建一个这样的Service,你需要让该类继承Service类,然后重写以下方法:
-
onCreate()
1.如果service没被创建过,调用startService()后会执行onCreate()回调;
2.如果service已处于运行中,调用startService()不会执行onCreate()方法。
也就是说,onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate(),此方法适合完成一些初始化工作。 -
onStartCommand()
如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用。 -
onStartCommand()方法很重要,我们在该方法中根据传入的Intent参数进行实际的操作,比如会在此处创建一个线程用于下载数据或播放音乐等。
-
onBind()
Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。 -
onDestory()
在销毁的时候会执行Service该方法。
这几个方法都是回调方法,且在主线程中执行,由android操作系统在合适的时机调用。
startService代码实例
创建TestOneService,并在manifest里注册。
需要注意,项目中的每一个Service都必须在AndroidManifest.xml中注册才行,所以还需要编辑AndroidManifest.xml文件,代码如下所示:
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.servicetest"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="14"
- android:targetSdkVersion="17" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- ……
- <service android:name="com.example.servicetest.TestOneService" >
- </service>
- </application>
- </manifest>
在MainActivty中操作TestOneService,code如下:
- public class TestOneService extends Service{
- @Override
- public void onCreate() {
- Log.i("Kathy","onCreate - Thread ID = " + Thread.currentThread().getId());
- super.onCreate();
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i("Kathy", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId());
- return super.onStartCommand(intent, flags, startId);
- }
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- Log.i("Kathy", "onBind - Thread ID = " + Thread.currentThread().getId());
- return null;
- }
- @Override
- public void onDestroy() {
- Log.i("Kathy", "onDestroy - Thread ID = " + Thread.currentThread().getId());
- super.onDestroy();
- }
- }
在MainActivity中三次startService,之后stopService。
[java] view plain copy
- /**
- * Created by Kathy on 17-2-6.
- */
- public class MainActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Log.i("Kathy", "Thread ID = " + Thread.currentThread().getId());
- Log.i("Kathy", "before StartService");
- //连续启动Service
- Intent intentOne = new Intent(this, TestOneService.class);
- startService(intentOne);
- Intent intentTwo = new Intent(this, TestOneService.class);
- startService(intentTwo);
- Intent intentThree = new Intent(this, TestOneService.class);
- startService(intentThree);
- //停止Service
- Intent intentFour = new Intent(this, TestOneService.class);
- stopService(intentFour);
- //再次启动Service
- Intent intentFive = new Intent(this, TestOneService.class);
- startService(intentFive);
- Log.i("Kathy", "after StartService");
- }
- }
打印出的Log如下
- 02-06 15:19:45.090 8938-8938/? I/Kathy: Thread ID = 1
- 02-06 15:19:45.090 8938-8938/? I/Kathy: before StartService
- 02-06 15:19:45.233 8938-8938/? I/Kathy: onCreate - Thread ID = 1
- 02-06 15:19:45.234 8938-8938/? I/Kathy: onStartCommand - startId = 1, Thread ID = 1
- 02-06 15:19:45.234 8938-8938/? I/Kathy: onStartCommand - startId = 2, Thread ID = 1
- 02-06 15:19:45.235 8938-8938/? I/Kathy: onStartCommand - startId = 3, Thread ID = 1
- 02-06 15:19:45.236 8938-8938/? I/Kathy: onDestroy - Thread ID = 1
- 02-06 15:19:45.237 8938-8938/? I/Kathy: onCreate - Thread ID = 1
- 02-06 15:19:45.237 8938-8938/? I/Kathy: onStartCommand - startId = 1, Thread ID = 1
- 02-06 15:19:45.238 8938-8938/? I/Kathy: after StartService
分析:
1.主线程打印出是1,所有回调方法中打印出的执行线程ID都是1,证明回调方法都是在主线程中执行的。
2.三次调用startService,只触发一次onCreate回调,触发了三次onStartCommand回调,且startId分别为1,2,3。证明 多次startService不会重复执行onCreate回调,但每次都会执行onStartCommand回调。
第二种方式:通过bindService启动Service
bindService启动服务特点:
1.bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
2.client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
3.bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁。
bindService代码实例
交互界面设计如下:


1.创建一个TestTwoService继承Service(Server)
2.创建ActivityA,可以通过bindService绑定服务(client)
3.创建ActivityB,可以通过bindService绑定服务(client)
4.ActivityA可以跳转到ActivityB
TestTwoService创建如下:
要想让Service支持bindService调用方式,需要做以下事情:
1.在Service的onBind()方法中返回IBinder类型的实例。
2.onBInd()方法返回的IBinder的实例需要能够返回Service实例本身。通常,最简单的方法就是在service中创建binder的内部类,加入类似getService()的方法返回Service,这样绑定的client就可以通过getService()方法获得Service实例了。
- public class TestTwoService extends Service{
- //client 可以通过Binder获取Service实例
- public class MyBinder extends Binder {
- public TestTwoService getService() {
- return TestTwoService.this;
- }
- }
- //通过binder实现调用者client与Service之间的通信
- private MyBinder binder = new MyBinder();
- private final Random generator = new Random();
- @Override
- public void onCreate() {
- Log.i("Kathy","TestTwoService - onCreate - Thread = " + Thread.currentThread().getName());
- super.onCreate();
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i("Kathy", "TestTwoService - onStartCommand - startId = " + startId + ", Thread = " + Thread.currentThread().getName());
- return START_NOT_STICKY;
- }
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- Log.i("Kathy", "TestTwoService - onBind - Thread = " + Thread.currentThread().getName());
- return binder;
- }
- @Override
- public boolean onUnbind(Intent intent) {
- Log.i("Kathy", "TestTwoService - onUnbind - from = " + intent.getStringExtra("from"));
- return false;
- }
- @Override
- public void onDestroy() {
- Log.i("Kathy", "TestTwoService - onDestroy - Thread = " + Thread.currentThread().getName());
- super.onDestroy();
- }
- //getRandomNumber是Service暴露出去供client调用的公共方法
- public int getRandomNumber() {
- return generator.nextInt();
- }
- }
client端要做的事情:
1.创建ServiceConnection类型实例,并重写onServiceConnected()方法和onServiceDisconnected()方法。
2.当执行到onServiceConnected回调时,可通过IBinder实例得到Service实例对象,这样可实现client与Service的连接。
3.onServiceDisconnected回调被执行时,表示client与Service断开连接,在此可以写一些断开连接后需要做的处理。
创建ActivityA,代码如下
- /**
- * Created by Kathy on 17-2-6.
- */
- public class ActivityA extends Activity implements Button.OnClickListener {
- private TestTwoService service = null;
- private boolean isBind = false;
- private ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- isBind = true;
- TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder) binder;
- service = myBinder.getService();
- Log.i("Kathy", "ActivityA - onServiceConnected");
- int num = service.getRandomNumber();
- Log.i("Kathy", "ActivityA - getRandomNumber = " + num);
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- isBind = false;
- Log.i("Kathy", "ActivityA - onServiceDisconnected");
- }
- };
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_a);
- Log.i("Kathy", "ActivityA - onCreate - Thread = " + Thread.currentThread().getName());
- findViewById(R.id.btnBindService).setOnClickListener(this);
- findViewById(R.id.btnUnbindService).setOnClickListener(this);
- findViewById(R.id.btnStartActivityB).setOnClickListener(this);
- findViewById(R.id.btnFinish).setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.btnBindService) {
- //单击了“bindService”按钮
- Intent intent = new Intent(this, TestTwoService.class);
- intent.putExtra("from", "ActivityA");
- Log.i("Kathy", "----------------------------------------------------------------------");
- Log.i("Kathy", "ActivityA 执行 bindService");
- bindService(intent, conn, BIND_AUTO_CREATE);
- } else if (v.getId() == R.id.btnUnbindService) {
- //单击了“unbindService”按钮
- if (isBind) {
- Log.i("Kathy",
- "----------------------------------------------------------------------");
- Log.i("Kathy", "ActivityA 执行 unbindService");
- unbindService(conn);
- }
- } else if (v.getId() == R.id.btnStartActivityB) {
- //单击了“start ActivityB”按钮
- Intent intent = new Intent(this, ActivityB.class);
- Log.i("Kathy",
- "----------------------------------------------------------------------");
- Log.i("Kathy", "ActivityA 启动 ActivityB");
- startActivity(intent);
- } else if (v.getId() == R.id.btnFinish) {
- //单击了“Finish”按钮
- Log.i("Kathy",
- "----------------------------------------------------------------------");
- Log.i("Kathy", "ActivityA 执行 finish");
- this.finish();
- }
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- Log.i("Kathy", "ActivityA - onDestroy");
- }
- }
创建ActivityB,代码如下:
- /**
- * Created by Kathy on 17-2-6.
- */
- public class ActivityB extends Activity implements Button.OnClickListener {
- private TestTwoService service = null;
- private boolean isBind = false;
- private ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- isBind = true;
- TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder)binder;
- service = myBinder.getService();
- Log.i("Kathy", "ActivityB - onServiceConnected");
- int num = service.getRandomNumber();
- Log.i("Kathy", "ActivityB - getRandomNumber = " + num);
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- isBind = false;
- Log.i("Kathy", "ActivityB - onServiceDisconnected");
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_b);
- findViewById(R.id.btnBindService).setOnClickListener(this);
- findViewById(R.id.btnUnbindService).setOnClickListener(this);
- findViewById(R.id.btnFinish).setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- if(v.getId() == R.id.btnBindService){
- //单击了“bindService”按钮
- Intent intent = new Intent(this, TestTwoService.class);
- intent.putExtra("from", "ActivityB");
- Log.i("Kathy", "----------------------------------------------------------------------");
- Log.i("Kathy", "ActivityB 执行 bindService");
- bindService(intent, conn, BIND_AUTO_CREATE);
- }else if(v.getId() == R.id.btnUnbindService){
- //单击了“unbindService”按钮
- if(isBind){
- Log.i("Kathy", "----------------------------------------------------------------------");
- Log.i("Kathy", "ActivityB 执行 unbindService");
- unbindService(conn);
- }
- }else if(v.getId() == R.id.btnFinish){
- //单击了“Finish”按钮
- Log.i("Kathy", "----------------------------------------------------------------------");
- Log.i("Kathy", "ActivityB 执行 finish");
- this.finish();
- }
- }
- @Override
- public void onDestroy(){
- super.onDestroy();
- Log.i("Kathy", "ActivityB - onDestroy");
- }
- }
测试步骤1
step1: 点击ActivityA的bindService按钮
step2: 再点击ActivityA的unbindService按钮
Log输出:
- 02-07 14:09:38.031 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main
- 02-07 14:09:39.488 1738-1738/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:09:39.488 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA 执行 bindService
- 02-07 14:09:39.496 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main
- 02-07 14:09:39.497 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main
- 02-07 14:09:39.500 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected
- 02-07 14:09:39.500 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -1046987376
- 02-07 14:09:50.866 1738-1738/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:09:50.866 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA 执行 unbindService
- 02-07 14:09:50.870 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA
- 02-07 14:09:50.871 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main
总结调用bindService之后发生的事情:
1.client执行bindService()
2.如果Service不存在,则Service执行onCreate(),onBind()
3.client实例ServiceConnection执行onServiceConnected()方法
总结调用unbindService之后发生的事情:
1.client执行unbindService()
2.client与Service解除绑定连接状态
3.Service检测是否还有其他client与其连接,如果没有Service执行onUnbind()和onDestroy()
测试步骤2
step1: 点击ActivityA的bindService按钮
step2: 再点击ActivityA的Finish按钮
Log 输出
- 02-07 14:49:16.626 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main
- 02-07 14:49:18.102 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:49:18.102 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 执行 bindService
- 02-07 14:49:18.105 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main
- 02-07 14:49:18.110 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main
- 02-07 14:49:18.112 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected
- 02-07 14:49:18.112 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -318399886
- 02-07 14:49:19.540 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:49:19.540 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 执行 finish
- 02-07 14:49:19.789 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onDestroy
- 02-07 14:49:19.798 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA
- 02-07 14:49:19.798 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main
总结:如果client销毁,那么client会自动与Service解除绑定。
测试步骤3
step1: 点击ActivityA的bindService按钮
step2: 点击ActivityA的startActivity B按钮,切换到ActivityB
step3: 点击ActivityB中的bindService按钮
step4: 点击ActivityB中的unbindService按钮
step5: 点击ActivityB中的Finish按钮
step6: 点击ActivityA中的unbindService按钮
得到Log:
- 02-07 14:55:04.390 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main
- 02-07 14:55:08.191 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:55:08.191 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 执行 bindService
- 02-07 14:55:08.197 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main
- 02-07 14:55:08.198 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main
- 02-07 14:55:08.205 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected
- 02-07 14:55:08.205 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -706215542
- 02-07 14:55:23.261 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:55:23.261 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 启动 ActivityB
- 02-07 14:55:29.239 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:55:29.239 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 执行 bindService
- 02-07 14:55:29.241 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - onServiceConnected
- 02-07 14:55:29.241 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - getRandomNumber = 1827572726
- 02-07 14:55:33.951 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:55:33.951 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 执行 unbindService
- 02-07 14:55:36.645 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:55:36.645 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 执行 finish
- 02-07 14:55:36.852 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - onDestroy
- 02-07 14:55:43.137 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
- 02-07 14:55:43.137 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 执行 unbindService
- 02-07 14:55:43.143 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA
- 02-07 14:55:43.143 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main
总结bindService的生命周期:
1.点击ActivityA的bindService按钮
第一次调用bindService会实例化TestTwoService,然后执行其onBind()方法,得到IBinder类型的实例,将其作为参数传入ActivityA的ServiceConnection的onServiceConnected方法中,标志着ActivityA与TestTwoService建立了绑定。
2.点击ActivityB中的bindService按钮
由于TestTwoService已处于运行状态,所以再次调用bindService不会重新创建它的实例,所以也不会执行TestTwoService的onCreate()方法和onBind()方法。ActivityB与ActivityA共享IBinder实例。此时有两个client与TestTwoService绑定。
3.点击ActivityB中的unbindService按钮
ActivityB与TestTwoService解除了绑定,当没有任何client与Service绑定时,才会执行Service的onUnbind()方法。此时,ActivityA还在绑定连接中,所以不会执行Service的解绑方法。
4.点击ActivityA中的unbindService按钮
ActivityA执行unbindService之后,ActivityA与TestTwoService就解除绑定了,这样就没有client与TestTwoService绑定,这时候Android会销毁TestTwoService,在销毁前会先执行TestTwoService的onUnbind()方法,然后才会执行其onDestroy()方法,这样TestService就销毁了。
4.4 service的生命周期
1. 生命周期常用方法
在Service的生命周期里,常用的有:
- 4个手动调用的方法
| 手动调用方法 | 作用 |
|---|---|
| startService() | 启动服务 |
| stopService() | 关闭服务 |
| bindService() | 绑定服务 |
| unbindService() | 解绑服务 |
- 5个内部自动调用的方法
| 内部自动调用的方法 | 作用 |
|---|---|
| onCreat() | 创建服务 |
| onStartCommand() | 开始服务 |
| onDestroy() | 销毁服务 |
| onBind() | 绑定服务 |
| onUnbind() | 解绑服务 |
2. 生命周期方法具体介绍
主要介绍内部调用方法和外部调用方法的关系。
2.1 startService()
- 作用:启动Service服务
- 手动调用startService()后,自动调用内部方法:onCreate()、onStartCommand()
- 调用逻辑如下:

2.2 stopService()
- 作用:关闭Service服务
- 手动调用stopService()后,自动调用内部方法:onDestory()
- 调用的逻辑:+

2.3 bindService()
- 作用:绑定Service服务
- 手动调用bindService()后,自动调用内部方法:onCreate()、onBind()
- 调用的逻辑:

2.4 unbindService()
- 作用:解绑Service服务
- 手动调用unbindService()后,自动调用内部方法:onCreate()、onBind()、onDestory()
- 调用的逻辑:

3. 常见的生命周期使用
3.1 只使用startService启动服务的生命周期

3.2 只使用BindService绑定服务的生命周期

3.3 同时使用startService()启动服务、BindService()绑定服务的生命周期

3.4 特别注意
- startService()和stopService()只能开启和关闭Service,无法操作Service;
bindService()和unbindService()可以操作Service
- startService开启的Service,调用者退出后Service仍然存在;
BindService开启的Service,调用者退出后,Service随着调用者销毁。
4.BroadCast
4.1 广播的定义

【说明】
【1】广播实现了不同程序之间的数据的共享,只要和发送广播的action相同的接收者都可以接收到广播,发送一个广播可以被许多的广播receiver接收;
【2】广播可以具有通知的作用;activity可以通过广播接收者直接接收从service中发送的数据,而不要让service和activity直接进行操作;
4.2 广播使用的场景

4.3 广播的种类

4.4 广播的接收者
【说明】有两种注册的方式:
【静态注册】注册完成后就会一直运行,即使是注册的activity已经销毁了(进程杀死了),但是仍然在一直运行,仍然可以收到广播;是在Androidmenifest.xml中声明的;
【动态注册】跟随Activity的生命周期,一旦activity的生命周期结束,则动态注册的广播接收者也消失;
[注意]在动态注册之后的记得在activity的onDestory中unRegisterBroadcastReceiver(),否则容易引起内存泄露;
【区别】
【1】注册方式不同;
【2】生命周期不同;


【源码查看】









5.WebView
【说明】

5.1 WebView 任意代码执行漏洞
出现该漏洞的原因有三个:
- WebView 中
addJavascriptInterface()接口 - WebView 内置导出的
searchBoxJavaBridge_对象 - WebView 内置导出的
accessibility和accessibilityTraversalObject 对象
addJavascriptInterface 接口引起远程代码执行漏洞
A. 漏洞产生原因
JS调用Android的其中一个方式是通过addJavascriptInterface接口进行对象映射:
webView.addJavascriptInterface(new JSObject(), "myObj");
// 参数1:Android的本地对象
// 参数2:JS的对象
// 通过对象映射将Android中的本地对象和JS中的对象进行关联,从而实现JS调用Android的对象和方法
所以,漏洞产生原因是:当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。
如可以执行命令获取本地设备的SD卡中的文件等信息从而造成信息泄露
具体获取系统类的描述:(结合 Java 反射机制)
- Android中的对象有一公共的方法:getClass() ;
- 该方法可以获取到当前类 类型Class
- 该类有一关键的方法: Class.forName;
- 该方法可以加载一个类(可加载 java.lang.Runtime 类)
- 而该类是可以执行本地命令的5.2 WebView在布局文件中的使用
应对方案:
APP研发者:
1. 确保只在访问可信页面数据时才使用addjavascriptInterface。
2. 在调用Java对象方法前对参数进行检查,避免执行恶意操作。
3. 对于在4.2(API 17+)系统运行的应用,使用JavascriptInterface代替addjavascriptInterface。
4. 限制对于该接口的使用来源,只允许可信来源访问该接口。例如使用WebViewClient中的shouldOverrideUrlLoading()来对加载的URL进行检查。
APP使用者:
1. 关注应用厂商更新情况,尽快升级应用程序到最新版本。
2. 在厂商修补前,用户应尽量避免使用应用浏览不可信的网页链接和邮件。
Android 官方:
Android官方已提醒此功能是有安全风险的,在可能访问不可信网页内容时需要小心处理。
Android 4.2 (api 17)已经开始采用JavascriptInterface代替addjavascriptInterface。
解决方案
- class JsObject {
- @JavascriptInterface
- public String toString() { return "injectedObject"; }
- }
- webView.addJavascriptInterface(new JsObject(), "injectedObject");
- webView.loadData("", "text/html", null);
- webView.loadUrl("javascript:alert(injectedObject.toString())");
- public boolean onJsPrompt(WebView view, String url, String message,
- String defaultValue, JsPromptResult result)
- javascript:(function JsAddJavascriptInterface_(){
- if (typeof(window.jsInterface)!='undefined') {
- console.log('window.jsInterface_js_interface_name is exist!!');}
- else {
- window.jsInterface = {
- onButtonClick:function(arg0) {
- return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));
- },
- onImageClick:function(arg0,arg1,arg2) {
- prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]}));
- },
- };
- }
- }
- )()
4,window.jsInterface这表示在window上声明了一个Js对象,声明方法的形式是:方法名:function(参数1,参数2)
- onLoadResource
- doUpdateVisitedHistory
- onPageStarted
- onPageFinished
- onReceivedTitle
- onProgressChanged
"hashCode",
"notify",
"notifyAll",
"equals",
"toString",
"wait",
【说明】一般的布局会将WebView动态的addView()添加到布局中,当离开的时候,需要销毁webView;
在Activity的OnDestory()方法中,一定要先将布局中的webView先remove();然后再调用WebView的removeAllViews();再销毁WebView;

5.3 jsBridge
什么是JSBridge
JSBridge:听其取名就是js和Native之前的桥梁,而实际上JSBridge确实是JS和Native之前的一种通信方式。
简单的说,JSBridge就是定义Native和JS的通信,Native只通过一个固定的桥对象调用JS,JS也只通过固定的桥对象调用Native。
5.4 网页加载完毕之后的数据

WebViewClient.onPageFinished()。你永远无法确定当WebView调用这个方法的时候,网页内容是否真的加载完毕了。
当前正在加载的网页产生跳转的时候这个方法可能会被多次调用,StackOverflow上有比较具体的解释
(How to listen for a Webview finishing loading a URL in Android?), 但其中列举的解决方法并不完美。
所以当你的WebView需要加载各种各样的网页并且需要在页面加载完成时采取一些操作的话,
可能WebChromeClient.onProgressChanged()比WebViewClient.onPageFinished()都要靠谱一些。
5.5 耗电
WebView后台耗电问题。当你的程序调用了WebView加载网页,WebView会自己开启一些线程(?),
如果你没有正确地将WebView销毁的话,这些残余的线程(?)会一直在后台运行,由此导致你的应用程序耗电量居高不下。
对此我采用的处理方式比较偷懒,简单又粗暴(不建议),
即在Activity.onDestroy()中直接调用System.exit(0),
使得应用程序完全被移出虚拟机,这样就不会有任何问题了。
5.6 硬件加速导致的页面渲染问题
切换WebView闪屏问题。如果你需要在同一个ViewGroup中来回切换不同的WebView(包含了不同的网页内容)的话,你就会发现闪屏是不可避免的。
这应该是Android硬件加速的Bug,如果关闭硬件加速这种情况会好很多,但无法获得很好的浏览体验,你会感觉网页滑动的时候一卡一卡的,不跟手。
5.7 WebView的内存泄露问题

【参考文章】https://www.jianshu.com/p/3e8f7dbb0dc7
(方法4划重点)。
Android混合开发时经常用到WebView加载html等页面,而WebView的内存泄漏就是最经常遇到的问题,尤其是当项目中需要用webview加载的页面比较多时。
即使当我退出页面时在我的BrowserActivity的onDestroy()方法中进行内存占用回收(如下图)但并没有效果:
mWebView.removeAllViews();
mWebView.destroy();
mWebView=null;
当我点开了多少条新闻内存中就存在多少个BrowserActivity的实例,说明我退出时这个BrowserActivity没有被回收,这样的话当我浏览的新闻比较多时,
内存就会累积存在一定的OOM风险,而且新闻界面一般存在大量图片,所以这个问题是必须要解决的。
1. new一个而不是在.xml中定义webview节点
attention:最初在写这篇的时候这一小节可能写的不够严谨,要是造成误解真是抱歉;
写这篇小结时的目的也是想把知道的一些解决方法记下来方便自己查看,没想到能收到评论和质疑,
我还是很开心的,但是通过这个我也发现,发出来的东西还是要写的严谨一些,会慢慢改进的。所以这一小节重新说明了一下,要是有不对的地方还是欢迎大家拍砖。
不要在布局文件中定义webview的节点,而是在需要的时候动态生成。你可以在需要webview的布局位置放一个LinearLayout,需要时在代码中动态生成webview并add进去:
//mWebView=new WebView(this);
mWebView=new WebView(getApplicationContext());
LinearLayout linearLayout = findViewById(R.id.xxx);
linearLayout.addView(mWebView);
然后在onDestroy()方法中调用:
@Override
protected void onDestroy() {
if( mWebView!=null) {
mWebView.setVisibility(View.GONE);
mWebView.removeAllViews();
mWebView.destroy();
}
super.onDestroy();
}
tips: 关于创建webview时new WebView(...);到底是传入ApplicationContext还是Activity的context,说法不一,但是网上较为一致的观点是采用application的context。
传ApplicationContext貌似可以防止webview对activity的引用而造成的内存泄漏;但是在很多情况下会报错,但是这个出错应该是webview的某些特殊动作产生由Application到Activity的类型转换错误;
采用activity的context细想来貌似和在xml中直接定义没有什么区别;
2. 手动删除引用
这个方法在我的项目中没有效果,但原文博主说在他的项目中效果很好,也许对其他人的情况有效,在这里也记下来。
public void setConfigCallback(WindowManager windowManager) {
try {
Field field = WebView.class.getDeclaredField("mWebViewCore");
field = field.getType().getDeclaredField("mBrowserFrame");
field = field.getType().getDeclaredField("sConfigCallback");
field.setAccessible(true);
Object configCallback = field.get(null);
if (null == configCallback) {
return;
}
field = field.getType().getDeclaredField("mWindowManager");
field.setAccessible(true);
field.set(configCallback, windowManager);
} catch(Exception e) {
}
}
然后在activity中调用:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setConfigCallback(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
}
public void onDestroy() {
setConfigCallback(null);
super.onDestroy();
}
3. 进程
为加载WebView的界面开启新进程,在该页面退出之后关闭这个进程。
这个方法我没有测试,不知道应用和效果如何,有兴趣的可以试试。
4. 从根源解决(划重点)
前面的方法都没有解决我内存泄漏的问题,然后我看到了一篇文章是从源码角度分析了webview内存泄漏的原因,最后按作者的方法解决了问题,后面会贴上原文地址。
这里简单说一下:
原文里说的webview引起的内存泄漏主要是因为org.chromium.android_webview.AwContents 类中注册了component callbacks,但是未正常反注册而导致的。
org.chromium.android_webview.AwContents 类中有这两个方法 onAttachedToWindow 和 onDetachedFromWindow;
系统会在attach和detach处进行注册和反注册component callback;
在onDetachedFromWindow() 方法的第一行中:
if (isDestroyed()) return;,
如果 isDestroyed() 返回 true 的话,那么后续的逻辑就不能正常走到,所以就不会执行unregister的操作;我们的activity退出的时候,
都会主动调用 WebView.destroy() 方法,这会导致 isDestroyed() 返回 true;
destroy()的执行时间又在onDetachedFromWindow之前,所以就会导致不能正常进行unregister()。
然后解决方法就是:让onDetachedFromWindow先走,在主动调用destroy()之前,把webview从它的parent上面移除掉。
ViewParent parent = mWebView.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mWebView);
}
mWebView.destroy();
完整的activity的onDestroy()方法:
@Override
protected void onDestroy() {
if( mWebView!=null) {
// 如果先调用destroy()方法,则会命中if (isDestroyed()) return;这一行代码,需要先onDetachedFromWindow(),再
// destory()
ViewParent parent = mWebView.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mWebView);
}
mWebView.stopLoading();
// 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错
mWebView.getSettings().setJavaScriptEnabled(false);
mWebView.clearHistory();
mWebView.clearView();
mWebView.removeAllViews();
mWebView.destroy();
}
super.on Destroy();
}
这个方法亲测有效。
原文地址:http://blog.csdn.net/xygy8860/article/details/53334476?utm_source=itdadao&utm_medium=referral
附上检查内存泄漏的工具:leakcanary
6.Binder


【用打电话原理进行通信的比喻】只限于通信,在通信之前需要注册;

【完整的流程】

【跨进程的模型】

【什么是Binder?】


【asInterface方法功能】如果是跨进程就是用代理对象,如果是同一个进程就使用本地的对象;





浙公网安备 33010602011771号