进入安卓的第一本书(简要概括)
第一章 了解大体Android
1.Android系统架构
- Linux内核层:为Android设备的各种硬件提供了底层的驱动.
- 系统运行库层:通过c/c++库来提供主要特性支持(数据库,浏览器内核,3D绘图),Android运行时库,Dalvik虚拟机(ART运行环境).
- 应用框架层:提供所需要的各种API.
- 应用层:手机上的应用程序皆属于该层.
2.Android应用开发特色
- 四大组件:活动(Activity),服务(Service),广播接收器(Broadcast Receiver),内容提供器(Content Provider).
- 丰富的系统控件
- SQLite数据库
- 强大的多媒体
- 地理位置定位
第二章 活动
1.定义
活动是一种可以包含用户界面的组件,主要用于和用户进行交互.
2.基本用法
MainActivity .java (业务逻辑)
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
activity_main.xml(内容展示)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
AndroidManifest.xml(文件注册)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3.活动状态
- 运行状态:位于栈顶位置
- 暂停状态不在处于栈顶位置,但可见
- 停止状态不在处于栈顶位置,不可见
- 销毁状态从返回栈移除
4.生命周期
- onCreate():第一次被创建时调用
- onStart():活动由不可见变为可见时调用
- onResume():与用户进行交互时调用,位于栈顶
- onPause():系统准备去启动或恢复另一个活动时调用
- onStop():活动完全不可见时调用
- onDestroy():活动被销毁前调用
- onRestart():有停止状态变为运行状态时调用
完整生存期:onCreate()
初始化~onDestroy()
释放内存
可见生存期:onStart()
对资源加载~onStop()
对资源释放
前台生存期:onResume()
~onPause()
进行交互
5.启动模式
需要先注册android:launchMode="xxx"
- standard:活动默认的启动模式,开启一个新活动,就放入返回栈顶层
- singleTop:启动活动时返回栈已是该活动,直接使用,不会创建新的
- singleTask:启动新活动时,先检查是否已存在,将存在之上的全部出栈,并直接使用
singleInstance:活动会启动一个新的返回栈来管理这个活动,共用同一个返回栈,解决共享活动实例的问题
6.使用API
1.活动中使用Toast
Toast.makeText(MainActivity.this, "You clicked Button 1", Toast.LENGTH_SHORT).show();
参数一:Context
是Toast要求的上下文,由于活动本身就是一个Context对象,所以可以直接传入.
参数二:Toast显示的文本内容.
参数三:Toast显示的时长.内置常量有Toast.LENGTH_SHORT
(2s)和Toast.LENGTH_LONG
(3.5s).
2.活动中使用Menu
1.需要建立一个菜单文件
<item
android:id="@+id/add_item"
android:title="Add" />
<item
android:id="@+id/remove_item"
android:title="Remove"/>
2.给当前创建菜单
@Override
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
参数一:指定我们通过哪一个资源文件来创建菜单.
参数二:用于指定我们的菜单项将添加到哪一个Menu对象当中.
return
中true/false分别表示创建的菜单显示/不显示.
3.菜单响应事件
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){//item.getItemId()是获取菜单栏点击项的对应ID
case R.id.add_item:
Toast.makeText(this,"You clicked Add",Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show();
break;
default:
}
return true;
}
3.销毁活动
finish();
4.Intent在活动中的使用
显式
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);
隐式
<activity
android:name=".SecondActivity"
android:exported="false" >
<intent-filter>
<action android:name="com.example.activitytest_java.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
Intent intent=new Intent("com.example.activitytest_java.ACTION_START");
startActivity(intent);
5.调用其他协议
百度
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
拨打电话
Intent intent=new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
6.传递数据
向下一活动传递
活动一:
String data="Hello SecondActivity";
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);
startActivity(intent);
活动二:
Intent intent=getIntent();
String data=intent.getStringExtra("extra_data");
返回数据上一活动
活动一:
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String returnedData = data.getStringExtra("data_return");
Log.d("FirstActivity", returnedData);
}
break;
default:
}
}
活动二:
Intent intent=new Intent();
intent.putExtra("data_return","Hello FirstActivity");
setResult(RESULT_OK,intent);
finish();
7.Back键
@Override
public void onBackPressed() {
Intent intent=new Intent();
intent.putExtra("data_return","Hello FirstActivity");
setResult(RESULT_OK,intent);
finish();
}
第三章 UI
1.定义
UI即User Interface(用户界面)的简称.UI设计则是指对软件的人机交互,操作逻辑,界面美观的整体设计.
2.常用控件
1.TextView(文本内容)
2.Button(按钮)
3.EditText(输入框)
4.ImageView(图片视图)
5.ProgressBar(进度条)
6.AlertDialog(弹出对话框)
7.ProgressDialog(弹出等待加载)
3.基本布局
1.线性布局(LinearLayout)
2.相对布局(RelativeLayout)
3.帧布局(FrameLayout)
4.百分比布局(PercentFrameLayout)
4.自定义
1.自定义控件
1.先建立自定义title.xml
2.引入布局到activity_main.xml中<include layout="@layout/title"/>
3.隐藏系统自带标题栏
ActionBar actionbar=getSupportActionBar();
if(actionbar!=null){
actionbar.hide();
}
2.控件响应
1.创建一个类继承一个布局,进行自定义控件的填充
2.在主布局文件中添加自定义控件
<com.example.uicustomviews.TitleLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
3.在对应的填充类中进行事件的注册编写.
5.ListView
1.系统内部自带
1.xml文件ListView的引入
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
2.使用系统内部的适配器
ArrayAdapter<String> adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1,data);
ListView listView=(ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
ArrayAdapter()
参数1:当前上下文
参数2:ListView子项布局的id
参数3:要是适配的数据
2.自定义ListView界面
1.实体类
public class Fruit{
private String name;
private int imageId;
public Fruit(String name,int imageId){
this.name=name;
this.imageId=imageId;
}
public String getName(){
return name;
}
public int getImageId(){
return imageId;
}
}
2.自定义布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/fruit_image"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"/>
</LinearLayout>
3.自定义适配器
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int textViewResourceId, @NonNull List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId=textViewResourceId;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Fruit fruit=getItem(position);//获取当前项的Fruit实例
//为子项加载传入的布局
View view= LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
ImageView fruitImage=(ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName=(TextView) view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
}
}
------------------------------优化------------------------------------------
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Fruit fruit = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
viewHolder=new ViewHolder();
viewHolder.fruitImage=(ImageView)view.findViewById(R.id.fruit_image);
viewHolder.fruitName=(TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder=(ViewHolder) view.getTag();
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder{
ImageView fruitImage;
TextView fruitName;
}
4.主活动调用
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();//初始化水果数据
FruitAdapter adapter=new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
ListView listView=(ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initFruits(){
for(int i=0;i<2;i++){
Fruit apple=new Fruit("Apple",R.drawable.abc_vector_test);
fruitList.add(apple);
...
}
}
}
5.添加点击事件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();//初始化水果数据
FruitAdapter adapter=new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
ListView listView=(ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fruit fruit=fruitList.get(position);
Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
}
6.RecyclerView
1.在app/build.gradle文件,dependencies闭包添加
implementation 'android.recyclerview:recyclerview:1.0.2'
2.控件使用
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_View"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
3.适配器 And 5.点击事件
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> mFruitList;
/**
* ViewHolder 描述了一个项目视图和关于它在 RecyclerView 中的位置的元数据。
* viewholder的作用是因为Android有个recycler的反复循环器,viewholder就是借助他来做到循环利用itemview。
* <p>
* 例如:
* <p>
* 默认加载10个itemview,初始化的时候创建一个viewholder,并把10个itemview加载到内存里面。
* <p>
* 接着下滑,加载5-15的item,这个时候,显示的还是只有10个view。
* <p>
* 我们只是需要重新填充view的数据,而不需要再次创建view并加载到内存里面,这样就可以复用itemview而避免频繁创建view导致的内存消耗了。
* <p>
* 结论:相当于只创建了一次view,其他时候都在复用view,只是更改了数据而已。
*/
static class ViewHolder extends RecyclerView.ViewHolder {
View fruitView;
ImageView fruitImage;
TextView fruitName;
public ViewHolder(@NonNull View view) {
super(view);
fruitView = view;
fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
fruitName = (TextView) view.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> fruitList) {
mFruitList = fruitList;
}
/**
* 当 RecyclerView 需要给定类型的新RecyclerView.ViewHolder来表示项目时调用。
* 这个新的 ViewHolder 应该用一个可以表示给定类型的项目的新视图来构造。您可以手动创建新视图,也可以从 XML 布局文件扩展它。
* 新的 ViewHolder 将用于使用onBindViewHolder(RecyclerView.ViewHolder, int, List)显示适配器的项目。由于它将被重新用于显示数据集中的不同项目,因此最好缓存对 View 的子视图的引用,以避免不必要的View.findViewById(int)调用。
* 参数:
* parent – 新视图绑定到适配器位置后将添加到的视图组。
* viewType – 新视图的视图类型。
* 回报:
* 一个新的 ViewHolder 持有给定视图类型的视图。
*/
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
//ViewHolder holder=new ViewHolder(view);
final ViewHolder holder = new ViewHolder(view);
--------------------点击事件开始---------------------
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(), "you clicked view" + fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
holder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(), "you clicked image" + fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
return holder;
}
--------------------点击事件结束---------------------
@Override
public void onBindViewHolder(@NonNull FruitAdapter.ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
/**
* 返回适配器持有的数据集中的项目总数。
* 回报:
* 此适配器中的项目总数
* */
@Override
public int getItemCount() {
return mFruitList.size();
}
}
4.使用
RecyclerView recyclerView=(RecyclerView) findViewById(R.id.recycler_View);
LinearLayoutManager layoutManager=new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter=new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
第四章 碎片
1.定义
碎片是一种可以嵌入在活动中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间.
2.生命周期
3 碎片的状态和回调
1.运行状态
当一个Fragment是可见的Activity正处于运行状态时,该Fragment也处于运行状态
2.暂停状态
当一个Activity进入暂停状态时(用于另一个未占满屏幕的Activity被添加到了栈顶),与它相关联的Fragment就会进入暂停状态
3.停止状态
当一个Activity进入停止状态时,与它相关联的Fragment就会进入停止状态,或通过调用FragmentTransaction的remove(),replace()方法将Fragment从Activity中移除,但在事务提交之前并没有调用addToBackStack()方法,这时的Fragment也会进入停止状态.总的来说,进入停止状态的Fragment对用户来说是完全不可见的,有可能会被系统回收.
4.销毁状态
Fragment总是依附于Activity而存在,因此当Activity被销毁时,与它相关联的Fragment就会进入销毁状态.或者通过调用FragmentTransaction的remove(),
replace()方法将Fragment从Activity中移除,但在事务提交之前并没有调用addToBackStack()方法,这时的Fragment也会进入销毁状态.
回调方法
onAttack():当Fragment和Activity建立关联时调用.
onCreateView():为Fragment创建视图(加载布局)时调用.
onActivityCreated():确保与Fragment相关联的Activity已经创建完毕时调用.
onDestroyView()"当与Fragment关联的视图被移除时调用.
onDetach():当Fragment和Activity解除关联时调用.
4.使用
静态使用
1.创建两个简单布局,分别作为左右两部分
2.创建左右两部分的java文件继承Fragment,进行对应xml文件填充
3.在主布局内部添加两个<fragment>
,分别为左右两部分,通过name标签引用java文件所对应的左右布局
动态使用
1.将主布局中右边的空间替换为一个布局
2.新建一个用于右侧显示的布局
3.使用java文件继承Fragment来填充新布局
4.主活动中进行动态替换
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=(Button) findViewById(R.id.button);
button.setOnClickListener((View.OnClickListener) this);
replaceFragment(new RightFragment());
}
public void onClick(View v){
switch (v.getId()){
case R.id.button:replaceFragment(new AnotherRightFragment());
break;
default:
break;
}
}
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction transaction=fragmentManager.beginTransaction();
transaction.replace(R.id.right_layout,fragment);
transaction.commit();
}
}
动态添加碎片分5步.
(1)创建待添加的碎片实例.
(2)获取FragmentManager,在活动中可以直接通过调用getSupportFragmentManager()方法得到.
(3)开启一个事务,通过调用beginTransacation()方法开启.
(4)向容器内添加或替换碎片,一般使用replace()方法实现,需要传入容器的id和待添加的碎片实例.
(5)提交事务,调用commit()的方法来完成.
5.碎片的API
1.将碎片添加到返回栈
FrafmentTransaction
中提供了一个addToBackStack()
方法,可以将事务添加到返回栈中.
2.碎片与活动的通信
为了方便碎片和活动之间进行通信,提供如下方法
活动中调用碎片:
RightFragment rightFragment=(RightFragment) getSupportFragmentManager().findFragmentById(R.id.right_fragment);
碎片中调用活动(也可调用Context)
MainActivity activity=(MainActivity) getActivity();
第五章 广播
1.定义
Android中的广播主要分为标准广播和有序广播.
1.标准广播 Normal broadcasts是一种完全异步执行的广播,在广播发出之后,所有的 广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可 言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。
2.有序广播 Orderedbroadcasts则是一种同步执行的广播,在广播发出之后,同一时刻 只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广 播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先 收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器 就无法收到广播消息了。
3.本地广播 localBroadcast这个机制发出的广播只能够在应用程序内部进行传递,并且广播的接收器也只能接受来自本应用程序发出的广播
2.接收广播
动态注册广播接收器的优点以及缺点:动态注册的广播接收器可以自由地控制注册与注销,
在灵活性方面有很大优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在 onCreate()方法中的。那么有没有广播能在程序未启动的情况下就能接收到广播呢?静态注册的广播接收器就可以做到
1.动态
1.通过代码来使用意图过滤器,进行获取监听网络的类型
2.注册接收方,通过继承BroadcastReceiver
,重写内部的onReceive()
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter=new IntentFilter();
//广播监听器所要监听的广播,添加相应的action
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//进行注册
networkChangeReceiver=new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver,intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注册
unregisterReceiver(networkChangeReceiver);
}
//每当网络状态发生变化时,onReceive()方法就会得到执行
class NetworkChangeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"network changes",Toast.LENGTH_SHORT).show();
}
}
}
2.静态
1.通过Android Studio自动创建一个Broadcast Receiver
2.AndroidManifest.xml自动注册了广播接收器
3.添加接受权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
获取接收广播的类型
<application
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
......
</application>
3.发送广播
1.标准广播
Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST");
sendBroadcast(intent);
2.有序广播
Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST");
sendOrderedBroadcast(intent,null);
设置先后顺序
在<intent-filter>
中设置 android:priority="100"
拦截广播
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
abortBroadcast();//拦截
}
}
4.本地广播
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
class NetworkChangeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager=(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo= connectivityManager.getActiveNetworkInfo();
if(networkInfo!=null&& networkInfo.isAvailable()){
Toast.makeText(context,"network changes",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(context,"network is unavailable",Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取实例
localBroadcastManager=LocalBroadcastManager.getInstance(this);
Button button=(Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent("com.example.broadcasttest.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);//发送本地广播
}
});
intentFilter=new IntentFilter();
intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
localReceiver=new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver,intentFilter);//注册本地广播监听其
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localReceiver);
}
class LocalReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show();
}
}
}
第六章 持久化
1.定义
数据持久化就是指将那些内存中的瞬时数据保存到储存设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失.保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的,持久化技术提供了一种机制可以让数据在瞬时状态和持久状态直接进行状态.
Android系统主要提供了3种方式实现数据持久化功能:文件储存,SharedPreferences存储,数据库存储.
2.文件存储
文件存储:他不对存储内容进行任何格式化处理,所有数据都是原封不动的保存到文件当中,比较适合存储一些简单的文本数据或二进制数据.
文件的存储和获取与Java的输入输出流方式基本一样.
3.SharedPreferences存储
获取SharedPreferences对象三种方式
1.Context类中的getSharedPreferences()方法
参数1:指定SharedPreferences文件名称,如果不存在则会创建一个,SharedPreferences文件都是存放在/data/data//shared_prefs/目录下的
参数2:用于指定操作模式,目前只有MODE_PRIVATE模式,为默认模式,和直接传入0效果是相同的,表示当前的应用程序才可以对这个SharedPreferences文件进行读写.其余模式已被废弃.
2.Activity类中的getPreferences()方法
该方法与Context中的getSharedPreferences()方法相似,不过它只接收一个操作模式参数,因为使用这种方式会自动将当前活动的类名作为SharedPreferences的文件名.
3.PreferenceManager类中的getDefaultSharedPreferences()方法
该方法为静态方法,它接收一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件.得到了SharedPreferences对象后,就可以开始向SharedPreferences文件存储数据了.
三步实现法:
(1)调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象.
(2)向SharedPreferences.Editor对象中添加数据.
(3)调用apply()方法将添加的数据提交,从而完成数据存储操作.
存储
SharedPreferences.Editor editor=getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name","Tom");
editor.putInt("age",28);
editor.putBoolean("married",false);
editor.apply();
获取
SharedPreferences pref=getSharedPreferences("data",MODE_PRIVATE);
String name=pref.getString("name","");
int age=pref.getInt("age",0);
boolean married=pref.getBoolean("married",false);
4.SQLite数据库存储
1.创建数据库
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK="create table Book("
+"id integer primary key autoincrement,"+"author text,"+"price real,"+"pages integer,"+"name text)";
private Context mContext;
//Context,数据库名,允许我们在查询数据时返回一个自定义Cursor,数据库版本号
public MyDatabaseHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version){
super(context, name, factory, version);
mContext=context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper=new MyDatabaseHelper(this,"BookStore.db",null,1);//数据库的创建信息
Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dbHelper.getWritableDatabase();//执行创建
}
});
}
}
2.升级数据库
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK="create table Book("
+"id integer primary key autoincrement,"+"author text,"+"price real,"+"pages integer,"+"name text)";
public static final String CREATE_CATEGORY="create table Category("+"id integer primary key autoincrement,"+"category_name text,"+"category_code integer)";
private Context mContext;
public MyDatabaseHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version){
super(context, name, factory, version);
mContext=context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
修改版本号
//将数据库版本号提升就可让onUpgrade()方法执行.
dbHelper=new MyDatabaseHelper(this,"BookStore.db",null,2);
3.添加数据
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("name","The Da Vinci Code");
values.put("author","Dan Brown");
values.put("pages",454);
values.put("price",16.96);
db.insert("Book",null,values);//插入第一条数据
4.更新数据
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("price",20.99);
db.update("Book",values,"name=?",new String[]{"The Da VinciCode"});
5.删除数据
SQLiteDatabase db=dbHelper.getWritableDatabase();
db.delete("Book","pages>?",new String[]{"500"});//插入第二条数据
6.查询数据
SQLiteDatabase db=dbHelper.getWritableDatabase();
//查询Book表中所有的数据
Cursor cursor=db.query("Book",null,null,null,null,null,null);
if (cursor.moveToFirst()){
do{
//遍历Cursor对象,取出数据并打印
String name=cursor.getString(cursor.getColumnIndex("name"));
String author=cursor.getString(cursor.getColumnIndex("author"));
int pages=cursor.getInt(cursor.getColumnIndex("pages"));
double price=cursor.getDouble(cursor.getColumnIndex("price"));
Log.d("MainActivity","book name is"+name);
Log.d("MainActivity","book author is"+author);
Log.d("MainActivity","book pages is"+pages);
Log.d("MainActivity","book price is"+price);
}while (cursor.moveToNext());
}
cursor.close();
7.使用SQL操作数据库
1.添加数据
db.execSQL(“insert into Book(name,author,pages,price)values(?,?,?,?)”,new String[]{“The Da Vinci Code”,“Dan Brown”,“454”,“16.96”});
2.更新数据
db.execSQL(“update Book set price=? where name=?”,new String[]{“10.99”,“The Da Vinci Code”});
3.删除数据
db.execSQL(“delete from Book where pages>?”,new String[]{“500”});
4.查询数据
db.rawQuery(“select *from Book”,null);
5.LitePal操作数据库
1.配置
implementation 'org.litepal.android:core:1.4.1'
2.配置litepal.xml文件
<dbname>
用于指定数据库名,<version>
用于指定数据库版本号<list>
用于指定所有的映射模型
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="BookStore"></dbname>
<version value="1"></version>
<list></list>
</litepal>
3.修改AndroidManifest.xml文件
<application
android:name="org.litepal.LitePalApplication"
......
/>
4.创建和升级数据库
1.定义一个数据类
book.java
2.在litepal.xml文件中配置映射模型
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="BookStore"></dbname>
<version value="1"></version>
<list>
<mapping class="com.example.litepaltest.Book"></mapping>
</list>
</litepal>
3.开始创建
LitePal.getDatabase();
如果想要添加数据列直接在实体类中添加相应的属性以及get,set方法
如果想要添加新的表只需要创建一个新类,再添加到映射模型中
<mapping class="com.example.litepaltest.Category"></mapping>
5.添加数据
进行CRUD时必须要让实体类继承 DataSupport
Book book=new Book();
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
book.setPress("Unknow");
book.save();//是DataSupport中所提供的方法,还有CRUD方法.
6.更新数据
Book book=new Book();
book.setPrice(14.95);
book.setPress("Anchor");
book.updateAll("name=?and author=?","The Lost Symbol","Dan Brown");
更新默认值
setToDefault()
方法可以让属性变为默认值.
Book book=new Book();
book.setToDefault("pages");
book.updateAll();
7.删除数据
DataSupport.deleteAll(Book.class,"price<?","15");
8.查询数据
List<Book> books=DataSupport.findAll(Book.class);
for(Book book:books){
Log.d("MainActivity","book name is"+book.getName());
Log.d("MainActivity","book author is"+book.getAuthor());
Log.d("MainActivity","book pages is"+book.getPages());
Log.d("MainActivity","book price is"+book.getPrice());
Log.d("MainActivity","book press is"+book.getPrice());
}
查询表中第一条数据
Book firstBook=DataSupport.findFirst(Book.class);
查询表中最后的数据
Book lastBook=DataSupport.findLast(Book.class);
select()方法用于指定查询那几列的数据
List<Book> books=DataSupport.select("name","author").find(Book.class);
where()方法用于指定查询的约束条件
List<Book> books=DataSupport.where("pages>?","400").find(Book.class);
order()方法指定结果的排序方式
List<Book> books=DataSupport.order("price desc").find(Book.class);//desc降序排序,asc升序排序
limit()方法用于指定查询结果的数量.
List<Book> books=DataSupport.limit(3).find(Book.class);
offset()方法用于指定查询结果的偏移量.
List<Book> books=DataSupport.limit(3).offset(1).find(Book.class);
也可以进行连缀组合
List<Book> books=DataSupport.select("name","author","pages")
.where("pages>?","400")
.order("pages")
.limit(10)
.offset(10)
.find(Book.class);
也支持原生的SQL查询
Cursor c=DataSupport.findBySQL("select * from Book where pages > ? and price < ?","400","20");
第七章 内容提供器
1.定义
内容提供器(Content Provider)主要用于在不同应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时环能保证被访问的数据的安全性.
2.申请权限
//判断用户是否已经授权.参数一Context,参数二:具体的权限名
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
//向用户申请权限,参数一:Activity实例,参数二:String数组,申请的权限名,参数三:请求码.
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, 1);
}
调用requestPermissions()
方法会回调onRequestPermissionsResult()
申请后的结果会存放到grantResults
中
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
call();
}else{
Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
3.ContentResolver基本用法
ContentResolver中的增删改操作都是通过Uri来进行的
Uri uri=Uri.parse("content://com.example.app.provider/table1")
1.查询
if(cursor!=null){
while(cursor.moveToNext()){
String column1=cursor.getString(cursor.getColumnIndex("column1"));
int column2=cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
2.增加
ContentValues values=new ContentValues();
values.put("column1","text");
values.put("column2",1);
getContentResolver().insert(uri,values);
3.修改
ContentValues values=new ContentValues();
values.put("column1","");
getContentResolver()..update(uri,values,"column1=? and column2=?",new String[]{"text","1"});
4.删除
getContentResolver().delete(uri,"column2=?",new String[]{"1"});
4.自定义内容提供器
public class MyProvider extends ContentProvider {
//初始化内容提供器.通常会在这里完成对数据库的创建和升级等操作,返回true表示内容提供其初始化成功,返回false这表示失败.
@Override
public boolean onCreate() {
return false;
}
//从内容提供器中查询数据.使用uri参数来确定那张表,projection参数用于确定查询那些列,selection和selectionArgs参数用于约束查询那些行,sortOrder参数用于对结果进行排序,查询结果存放在Cursor对象中返回.
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
return null;
}
//根据传入的内容URL来返回相应的MIME类型
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
//向内容提供器添加一条数据.使用url参数来确定更新那一张表中的数据,新数据保存在values参数中,selection和selectionArgs参数用于约束更新那些行,受影响的行数将作为返回值.
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
return null;
}
//从内容提供器中删除数据.使用uri参数来确定拿一张表中的数据,selection和selectionArgs参数用于约束删除那些行,被删除的行数作为返回值返回.
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
}
//更新内容提供器中已有的数据.使用uri参数来确定更新那一张表中的数据,新数据保存在values参数中,selection和selectionArgs参数用于约束更新那些行,收影响的行数将作为返回值返回.
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
}
一个标准的Uri写法
content"//com.example.app.provider/table1
表示调用方期望访问的是:com.example.app
这个应用的table1表中的数据.
content"//com.example.app.provider/table1/1
:表示调用方期望访问的是:com.example.app
这个应用的table1表中id为1的数据
*
:表示匹配任意长度的任意字符串
#
:表示匹配任意长度的数字
一个能够匹配任意表的内容URL格式:
content"//com.example.app.provider/*
一个能够匹配table表中任意一行数据的内容URL格式:
content"//com.example.app.provider/table/#
UriMatcher类各系实现匹配内容URL的功能
public class MyProvider extends ContentProvider {
public static final int TABLE1_DIR=0;
public static final int TABLE1_ITEM=1;
public static final int TABLE2_DIR=2;
public static final int TABLE2_ITEM=3;
public static UriMatcher uriMatcher;
static {
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider","table1",TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider","table1/#",TABLE1_ITEM);
uriMatcher.addURI("com.example.app.provider","table2",TABLE2_DIR);
uriMatcher.addURI("com.example.app.provider","table2/#",TABLE2_ITEM);
}
...
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
switch (uriMatcher.match(uri)){
case TABLE1_DIR:
//查询table1表中的所有数据
break;
case TABLE1_ITEM:
//查询table1表中的单条数据
break;
case TABLE2_DIR:
//查询table2表中的所有数据
break;
case TABLE2_ITEM:
//查询table2表中的单条数据
break;
default:
break;
}
...
}
...
}
一个内容URL所对应的MIME字符串格式:
1.必须以vnd开头.
2.如果内容URL以路径结尾,则后接android.cursor.dir/
,如果内容URL以id结尾,则后接android.cursor.item/
.
3.最后接上vnd.,
content"//com.example.app.provider/table1
的对应MIME类型可写为
vnd.android.cursor.dir/vnd.com.example.app.provider.table1
content"//com.example.app.provider/table1/1
的对应MIME类型可写为
vnd.android.cursor.item/vnd.com.example.app.provider.table1
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
case TABLE1_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
case TABLE2_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
default:
break;
}
return null;
}
第八章 多媒体
1.通知的使用
1.定义
通知(notification)当某个应用程序希望向用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知来实现.
2.基本使用
public class MainActivity extends AppCompatActivity implements View.onClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendNotice=(Button) findViewById(R.id.send_notice);
sendNotice.setOnClickListener(this);
}
public void onClick(View v){
switch(v.getId()){
case R.id.send_notice:
Intent intent=new Intent(this,NotificationActivity.class);
PendingIntent pi=PendingIntent.getActivity(this,0,intent,0);
NotificationManager manager=(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification=new NotificationCompat().Builder(this)
.setContentTitle("This is content title")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitampFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pi)
.setAutoCancel(true)//自动消失
.build();
manager.notify(1,notification);
break;
default:
break;
}
}
}
2.其余的调用摄像头和相册,播放音频和视频
第九章 网络技术
1.WebView的用法
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webview=(WebView) findViewById(R.id.web_view);
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebViewClient(new WebViewClient());
webview.loadUrl("http://www.baidu.com");
}
}
AndroidManifest.xml授权
<uses-permission android:name="android.permission.INTERNET"/>
2.HttpURLConnection
1.获取HttpURLConnection实例
URL url=new URL("https://www.baidu.com");
connection=(HttpURLConnection)url.openConnection();
2.设置请求方式
connection.setRequestMethod("GET");
-----------------------------------
connection.setRequestMethod("POST");
DataOutputStream out=new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
3.连接超时,读取超时毫秒数,自由定制的
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
4.获取服务器返回的输入流
InputStream in=connection.getInputStream();
5.HTTP连接关闭
connection.disconnect();
3.使用OkHttp
添加依赖库
implementation 'com.squareup.okhttp3:okhttp:4.1.0'
1.创建OkHttpClient实例
OkHttpClient client=new OkHttpClient();
2.创建一个Request对象
Request request=new Request.Builder().build();
3.丰富Request对象
Request request=new Request.Builder().
.url("http://www.baidu.com")
build();
4.发送请求并获取服务器返回的数据
Response response=client.newCall(request).execute();
5.获取具体内容
String responseData=response.body().string();
如果是发起一条post请求
Request request=new Request.Builder()
.url("http://www.baidu.com")
.post(requestBody)
.build();
4.解析XML格式
1.Pull解析方式
2.SAX解析方式
5.解析JSON格式数据
1.使用JSONObject
2.使用GSON
第十章 服务
1.定义
1.服务(Service)是Android中实现程序后台运行的解决方案,适合执行那些不需要和用户交互而且还要求长期运行的任务.服务的运行不依赖于任何用户界面,及程序被切换到后台,或用户打开了另外一个应用程序,服务仍然能够保持正常运行.
2.服务并不是运行在一个独立的进程中的,而是依赖于创建服务是所在的应用程序.当某个程序被抹杀,服务仍然能够保持正常运行.
3.实际服务不会自动开启线程,所有代码都是默认运行在主线程当中的.我们需要在服务的内部手动创建子线程,并在这里执行具体任务,否则就可能出现主线程被阻塞住的情况.
2.解析异步消息处理机制
Android的异步消息处理有4部分组成
1.Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据.
2.Handler
Handler顾名思义就是处理者的意思,它主要是用于发送和处理消息的,发送消息一般是使用Handler的sendMessage()方法,post()方法等,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中.
3.MessageQueue
MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息.这部分消息会一直存在于消息队列中,等待被处理.每个线程中只会有一个MessageQueue对象.
4.Looper
Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将他取出,并传递到Handler的handleMessage()方法中.每个线程中只会有一个Looper对象.
基本流程
1.在主线程中创建一个Handler对象,重写handleMessage()方法.
2.当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去.
3.这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中.
由于Handler构造函数中我们传入了Looper.getMainLooper(),所以此时handleMessage()方法中的代码也会在主线程中运行.
3.消息异步实例
Android不允许在子线程中进行UI操作.可以通过Android提供的一套异步消息处理机制,完善解决在子线程中进行UI操作问题.
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
public static final int UPDATE_TEXT=1;
private TextView text;
private Handler handler=new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
//在这里可以进行UI操作
text.setText("Nice to meet you");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text=(TextView) findViewById(R.id.text);
Button changeText=(Button) findViewById(R.id.change_text);
changeText.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.change_text:
new Thread(new Runnable() {
@Override
public void run() {
Message message=new Message();
message.what=UPDATE_TEXT;
handler.sendMessage(message);//将Message对象发送出去
}
}).start();
break;
default:
break;
}
}
}
4.AsyncTask
AsyncTask是Android提供方便将子线程切换到主线程的工具.实现原理也是基于异步消息处理机制.
由于AsyncTask是一个抽象类,所以需要子类去继承.在继承时我们可以为AsyncTask类指定3个泛型参数.
Params
在执行AsyncTask时需要传入的参数,可用于在后台任务中使用.
Progress
后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位.
Result
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回类型.
class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
...
}
参数一:Void
表示在执行AsyncTask的时候不需要传入参数给后台任务;
参数二:Integer
表示使用整型数据来作为进度显示单位;
参数三:Boolean
表示使用布尔型数据来反馈执行结果;
重写方法
1.onPreExecute()
该方法在后台任务开始执行之前调用,用于进行一些界面上的初始化操作(显示一个进度条对话框).
2.doInBackground(Params...)
该方法中所有的代码都会在子线程中运行,我们应该在这里处理所有的耗时任务.任务一旦完成就可以通过return语句来将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Void
,就可以不返回任务执行结果.在该方法中是不可以进行UI操作的,如果需要更新UI元素,可以调用publishProgress(Progress…)方法来完成.
3.onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress(Progress...)
方法后,onProgressUpdate(Progress...)
方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的.在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新.
4.onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用.返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等.
public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show();//显示进度对话框
}
@Override
protected Boolean doInBackground(Void... voids) {
try {
while (true){
int downloadPercent=doDownload();//这是一个虚构方法
publishProgress(downloadPercent);
if(downloadPercent>=100){
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
//在这里更新下载进度
progressDialog.setMessage("Downloaded"+values[0]+"%");
}
@Override
protected void onPostExecute(Boolean aBoolean) {
progressDialog.dismiss();//关闭进度对话框
//在这里提示下载结果
if(result){
Toast.makeText(context, "Download succeeded", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(content, "Download failed", Toast.LENGTH_SHORT).show();
}
}
}
该任务的启动new DownloadTask().execute();
诀窍:doInBackground()
方法执行具体的耗时任务
onProgressUpdate()方法进行UI操作
onPostExecute()
方法执行一些任务的收尾工作.
5.活动的生命周期
一旦在项目的任何位置调用了Context的startService()
方法,相应的服务就会启动起来,并回调onStartCommand()
方法.如果这个服务之间还没有创建过,onCreate()
方法会先于onStratCommand()
方法执行.服务启动了之后会一直保持运行状态,直到stopService()
或stopSelf()
方法被调用.注意,虽然每调用一次startService()
方法,onStartCommand()
就会执行一次,但实际上每个服务都只会存在一个实例.所以不管你调用了多少次startService()
方法,只需要调用一次stopService()或stopSelf()
方法,服务就会停止下来.
还可以调用Context的bindService()
来获取一个服务的持久联系,这时就会回调服务中的onBind()
方法.类似地,如果这个服务之间还没有创建过,onCread()
方法会先于onBind()
方法执行.之后,调用方可以获取到onBind()方法里返回的IBidner对象的实例,这样就能自由地和服务进行通信了.只要调用方和服务之间的链接没有断开,服务就会一直保持运行状态.
当调用了startService()
方法,这时服务中的onDestroy()
方法就会执行,表示服务已经销毁了,类似地,当调用了bindService()
方法后,又去调用unbindService()
方法,onDestory()
方法也会执行,这两种情况都很好理解.但是需要注意,我们是完全有可能对一个服务器调用了startService()
方法,又调用了bindService()
方法的,这种情况下该如何才能让服务销毁掉呢?根据Android系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁.所以这种情况下要同时调用stopService()
和unbindService()
方法,onDestroy()
方法才会执行.
6.服务的基本用法
1.建立一个服务
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
//服务创建时候调用
@Override
public void onCreate() {
super.onCreate();
}
//服务启动时调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
//服务销毁
@Override
public void onDestroy() {
super.onDestroy();
}
}
需要在AndroidManifest.xml进行注册
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
</service>
2.启动和停止服务
Intent startIntent=new Intent();
startService(startIntent);//启动服务
Intent stopIntent=new Intent(this,MyService.class);
stopService(stopIntent);//停止服务
3.活动和服务进行通信
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
downloadBinder=(MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService=(Button) findViewById(R.id.start_service);
Button stopService=(Button) findViewById(R.id.stop_Service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
Button bindService=(Button) findViewById(R.id.bind_service);
Button unbindService=(Button) findViewById(R.id.unbind_service);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.start_service:
Intent startIntent=new Intent();
startService(startIntent);//启动服务
break;
case R.id.stop_Service:
Intent stopIntent=new Intent(this,MyService.class);
stopService(stopIntent);//停止服务
break;
case R.id.bind_service:
Intent bindIntent=new Intent(this,MyService.class);
bindService(bindIntent,connection,BIND_ABOVE_CLIENT);//绑定服务,会回调onBind方法通过返回的mBinder,作为onServiceConnected的service参数
break;
case R.id.unbind_service:
unbindService(connection);//解除服务
break;
default:
break;
}
}
}
7.服务的更多技巧
1.使用前台服务
在Notification
对象后调用startForeground()
会让服务变为一个前台服务,并在系统状态栏显示出来.
2.使用IntentService
服务中的代码都是默认运行在主线程当中的,为了避免在服务里处理耗时的逻辑,出现ANR.因此需要用到Android多线程编程,在服务的每个具体的方法里开起一个子线程,用来处理耗时的逻辑.
public class MyService extends Service{
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable(){
@Override
public void run(){
//处理具体的逻辑
stopSelf();//该方法是让服务执行完毕后自动停止的功能
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
}
该线程会自动停止
public class MyIntentService extends IntentService {
public MyIntentService(String name) {
super("MyIntentService");//调用父类的有参构造器
}
//该方法是子线程运行
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//打印当前线程的id
Log.d("MyIntentService","Thread id is"+Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService","onDestroy executed");
}
}