Android复习
一.日志工具(Log)
Log.v():用于打印那些最为琐碎的,意义最小的日志信息。对应级别:verbose
Log.d() :打印调试信息。对应级别:debug
Log.i():打印比较重要的信息。对应级别:info
Log.w():打印警告信息。对应级别:warn
Log.e():打印程序中的错误信息。对应级别:error
String TAG=“test”;
Log.d(TAG,"打印调试信息");
二.活动(Activity)
1.使用Toast提醒消息:
Toast.makeText(MainActivity.this,"通知消息",Toast.LENTH_SHORT).show();
2.使用Intent在活动之间穿梭(实现页面跳转)
//显式Intent
Intent intent=new Intent(FirstActivity.this,SecondActivity.this);
startActivity(intent);
//隐式Intent
//在注册文件manifest.xml中声明需要跳转的界面的action和category
//每个Intent只能指定一个action,能指定多个category
<activity android:name=".SeconActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_ATART">
<category android:name="android.intent.category.DEFAULT">
<category android:name="com.example.activitytest.MY_CATEGORY">
</intent-filter>
</activity>
//在主界面有:
Intent intent=new Intent("com.example.activitytest.ACTION_ATART");
intent.addCategory("com.example.activitytest.MY_CATEGORY");//使用代码添加category时,必须在mannifest里面注册
startActivity(intent);
//使用Intent打开网页
Intent intent=new Intent(Intent.ACTION_VIEW);//Intent.ACTION_VIEW是android内置的一个动作
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
//使用Intent拨打电话
//必须加tel:
//geo显示地理协议
Intent intent=new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
//使用Intent发短信
Uri uri=Uri.parse("smsto:10086");
Intent intent new Intent(intent.ACTION_SENDTO,uri);
intent.putExtra("sms_body","Hello");
startActivity(intent);
3.使用Intent传递数据
从FirstActivity传值到SecondActivity
//传递数据使用putExtra()方法
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data","data");//第一个参数是键名,第二个参数是键值(真正传递的数据)
startActivity(intent);
//接收数据
Intent intent=getIntent();//通过getIntent()方法获取用于启动SecondActivity的Intent
String data=intent.getStringExtra("extra_data");//传递的整形则是getIntExtra(),布尔型则是getBooleanExtra()
从SecondActivity回传数据到FirstActivity
//在FirstActivity中有:
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);//第二个参数是请求码,必须唯一
//在SecondActivity中有:
Intent intent=new Intent();
intent.putExtra("data_return","hello FirstActivity");
setResult(RESULT_OK,intent);//第一个参数是回调码
finish();//销毁当前活动
//在FirstActivity中书写获取回调码的后续逻辑
//直接输入onActivityResult
@Override
protected void onActivityResult(int requstCode,int resultCode,Intent data){
switch(requstCode){
case 1:
if(resultCode==RESULT_OK){
String returnData=data.getStringExtra("data_return");
}
break;
default:
}
}
4.使用Bundle和序列化类传递数据
使用Bundle传递数据
//在FirstActivity中有
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
Bundle bundle=new Bundle();
bundle.putExtra("userNo","001");
bundle.putExtra("userName","CSL");
intent.putExtra(bundle);
startActivity(intent);
//在SecondActivity获取数据
Bundle bundle=this.getIntent().getExtras();
String userNo=bundle.getString("userNo");
序列化类传递数据
//
public class UserInfo implements Serializable{
private String userNo;
private String userName;
public String getUserNo(){return userNo;}
public String setUserNo(String userNo){this.userNo=userNo;}
public String getUserName(){return userName;}
public String setUserName(String userName){this.userName=userName;}
}
//在FirstActicity录入数据
UserInfo user=new UserInfo();
user.setUserno("001");
user.setUserName("CSL");
intent.putExtra("user",user);
startActivity(intent);
//在SecondActivity获取数据
UserInfo user=(UserInfo)this.getIntent().getSerializableExtra("user")
5.活动的生命周期
Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称为返回栈(Back
Stack)。在默认情况下,每当启动一个新的活动,它会在Back
Stack中入栈,并处于栈顶的位置。当按下Back键或者调用finish()方法时,处于栈顶的活动会出栈,使得前一个入栈的活动处于栈顶。系统总是会显示处于栈顶的活动。
4.1活动状态:
每个活动在其生命周期最多可能有4种状态
-
01.运行状态
当一个活动处于Back Stack的栈顶时,该活动就处于运行状态。
-
02.暂停状态
当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。在内存低的情况下,不能系统killed(杀死)
-
03.停止状态
当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。在内存低的情况下,能系统killed(杀死)
-
04.销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。
4.2活动的生存期:
7个回调方法
- onCreate()
- onStart()
- onResume()
- onPause()
- onStop()
- onDestroy()
- onRestart()
4.3Android的四种启动模式
指定启动模式:在注册文件中在标签中通过android:launchMode属性选择启动模式
-
standard模式:Activity的默认启动方式,每启动一个Activity就会在栈顶创建一个新的实例
-
singleTop模式:判断要启动的Activity实例是否位于栈顶,如果位于栈顶则直接复用,否则创建新的实例
-
singleTask模式:系统首先检查栈中是否存在当前Activity实例,如果存在则直接使用,并把当前Activity之上的所有实例全部出栈
-
singleInstance模式:启动一个新的任务战来管理Activity实例,无论从哪个任务战中启动该Activity,该实例在整个系统中只有一个
三.UI布局及控件
1.五种常见布局
- 线性布局:以水平或者垂直方向排列
- 相对布局:通过相对定位排列
- 帧布局:开辟空白区域,帧里的控件(层)叠加,帧布局的大小由内部最大控件决定
- 表格布局:表格形式排列。配合TableRow使用
- 网格布局:与表格类似以行列排列
- 绝对布局:通过x,y坐标排列
2.常用单位
- px:像素
- pt:磅数
- dp:基于屏幕密度的抽象单位
- sp:可伸缩像素,推荐设置文字大小时使用
3.控件
控件太多,属性等详情查找资料即可
3.1创建自定义控件
3.2ListView
见碎片Fragment新闻例子处
4.常见对话框
//普通对话框
//声明对象并创建对话框
final AlertDialog alertDialog=new AlertDialog.Builder(MainActivity.this).create();
//设置相关信息
alertDialog.setTitle("对话框");
alertDialog.setMessage("是否确定退出?");
alertDialog.setIcon(R.mipmap.ic_launcher);
//显示对话框
alertDialog.show();
//单选对话框
new AlertDialog.Builder(MainActivity.this)
.setTitle("请选择性别")//设置选项内容,0标识默认选中第一项
.setSingleChoiceItems(new String[]{"男", "女"}, 0,
new DialogInterface.OnClickListener() {
@Override//建立监听,允许被点击
public void onClick(DialogInterface dialog, int which) {
}
}).setPositiveButton("确定",null)
.show();
//多选对话框
new AlertDialog.Builder(MainActivity.this)
.setMultiChoiceItems(new String[]{"旅游","美食","汽车","宠物"},null,null)
.setPositiveButton("确定",null)
.show();
//进度条对话框
final ProgressDialog progressDialog;
progressDialog=new ProgressDialog(MainActivity.this);
progressDialog.setTitle("进度条对话框");
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.show();
四.碎片(Fragment)
1.新闻例子(静态添加Fragment和ListView的用法)
静态添加Fragment是通过布局文件添加
以新闻布局为例
当手机横屏时,布局为左边新闻列表,右边新闻详情,竖屏时,为双页模式
//新闻类
class News{
public int nID;
public int picture;
public String title;
public String content;
public String Source;
public String Time;
public News(String title,String content,String source,String Time,int picture,int nID){
this.title=title;
this.content=content;
this.Source=source;
this.Time=Time;
this.picture=picture;
this.nID=nID;
}
public int getPicture() { return picture; }
public String getContent() {
return content;
}
public String getSource() {
return Source;
}
public String getTime() {
return Time;
}
public String getTitle() {
return title;
}
public int getnID() { return nID; }
}
创建布局文件和Fragment:
创建两个Fragment(新闻列表碎片:ListFragment,新闻详情碎片:DetailFragment)
创建新闻列表Fragment(使用了ListView控件)
首先在fragment_list.xml里写一个listview布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</LinearLayout>
为每个ListView里面每列写布局:item.xml(此处为一个新建的单独的布局文件)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/news"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="5dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/news_picture"
android:layout_width="99dp"
android:layout_height="99dp"
android:src="@mipmap/ic_launcher"></ImageView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="99dp"
android:orientation="vertical">
<TextView
android:id="@+id/news_title"
android:text="新闻标题新闻标题"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="#000000"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical"></TextView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="49dp"
android:layout_weight="2"
android:orientation="horizontal">
<TextView
android:id="@+id/news_source"
android:text="来源:新华网"
android:layout_weight="1"
android:textSize="15sp"
android:textColor="#000000"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"></TextView>
<TextView
android:id="@+id/news_time"
android:text="时间:2020/09/28"
android:layout_weight="1"
android:textSize="15sp"
android:textColor="#000000"
android:layout_width="match_parent"
android:layout_height="20dp"
android:gravity="center_vertical"></TextView></LinearLayout>
</LinearLayout>
</LinearLayout>
现在写新闻详情的Fragmment,detail_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_margin="10dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="100dp">
<TextView
android:id="@+id/news_title"
android:text="新闻标题新闻标题"
android:textSize="30sp"
android:textStyle="bold"
android:textColor="#000000"
android:layout_width="match_parent"
android:layout_height="70dp"
android:gravity="center"></TextView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_weight="2"
android:orientation="horizontal">
<TextView
android:id="@+id/news_source"
android:text="来源:新华网"
android:layout_weight="1"
android:textSize="15sp"
android:textColor="#000000"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="left|center_vertical"></TextView>
<TextView
android:id="@+id/news_time"
android:text="时间:2020/09/28"
android:layout_weight="1"
android:textSize="15sp"
android:textColor="#000000"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="right|center_vertical"></TextView></LinearLayout></LinearLayout>
<View
android:background="#000000"
android:layout_width="match_parent"
android:layout_height="1dp"></View>
<TextView
android:id="@+id/news_content"
android:text="新闻内容新闻内容……"
android:textColor="#000000"
android:textSize="20sp"
android:layout_width="match_parent"
android:layout_height="match_parent"></TextView></LinearLayout>
碎片写完后,开始写布局
由于分横竖屏,横屏和竖屏对应的布局不一样,故有两个main_activity,xml文件
首先竖屏布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<fragment
android:id="@+id/fragmentList"
android:name="com.example.labfourfragment.ListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
</fragment>
</LinearLayout>
创建横屏界面的方法:
在layout目录旁再创建一个同级别的layout-land目录,将activity_list.xml布局文件复制到layout-land中即可
故land\main_activity.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_weight="5"
xmlns:android="http://schemas.android.com/apk/res/android">
<fragment
android:id="@+id/fragmentList"
android:layout_weight="2"
android:name="com.example.labfourfragment.ListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"></fragment>
<fragment
android:id="@+id/fragment_content"
android:layout_weight="3"
android:name="com.example.labfourfragment.DetailFragment"
android:layout_width="0dp"
android:layout_height="match_parent">
</fragment></LinearLayout>
对于detail_activity.xml有:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<fragment
android:id="@+id/fragment_content"
android:name="com.example.labfourfragment.DetailFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"></fragment>
</LinearLayout>
布局文件全部创建完毕,在.java文件中写逻辑:
写碎片的逻辑,新闻列表碎片(ListFragment.java)如下:
public class ListFragment extends Fragment {
public ListFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_list, container, false);
}
}
新闻详情碎片(DetailFragment.java):
public class DetailFragment extends Fragment {
public DetailFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_detail,container,false);
return view;
}
public void refresh(String news_title, Object news_content, Object news_sourse, Object news_time){
View v=getView().findViewById(R.id.fragment_content);
v.setVisibility(View.VISIBLE);
TextView news_Title=(TextView)v.findViewById(R.id.news_title);
TextView news_Content=(TextView)v.findViewById(R.id.news_content);
TextView news_Sourse=(TextView)v.findViewById(R.id.news_source);
TextView news_Time=(TextView)v.findViewById(R.id.news_time);
news_Title.setText(news_title);
news_Content.setText((CharSequence) news_content);
news_Sourse.setText((CharSequence) news_sourse);
news_Time.setText((CharSequence) news_time);
}
}
竖屏时,点击新闻跳转到新闻详情界面,故DetailActivity逻辑如下:
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
//获取数据
Intent intent=getIntent();
TextView DetailNewsContent=findViewById(R.id.news_content);
TextView DetailNewsTitle=findViewById(R.id.news_title);
TextView DetailNewsSource=findViewById(R.id.news_source);
TextView DetailNewsTime=findViewById(R.id.news_time);
DetailNewsContent.setText(intent.getStringExtra("newsContent"));
DetailNewsTitle.setText(intent.getStringExtra("newsTitle"));
DetailNewsSource.setText(intent.getStringExtra("newsSourse"));
DetailNewsTime.setText(intent.getStringExtra("newsTime"));
}
}
MainActivity.java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
setContentView(R.layout.activity_main);
//新闻信息:
final News[] news=new News[2];
news[0]=new News("西南石油大学新生军训","新生冬季军训","swpu","2020/10/07",R.mipmap.swpu,31062101);
news[1]=new News("西南石油大学冬季运动会","计科院学生积极参与","swpu","2020/10/18",R.mipmap.swpu_head,31062101);
final ListView listView=findViewById(R.id.listview);
//创建一个list集合
final List<Map<String,Object>> listItems=new ArrayList<Map<String,Object>>();
//通过for循环将图片id和列表项文字放在Map中,并添加到List集合中
for(int i=0;i<2;i++){
Map<String,Object>map=new HashMap<String, Object>();//实例化map对象
map.put("图标",news[i].getPicture());
map.put("标题",news[i].getTitle());
map.put("来源",news[i].getSource());
map.put("时间",news[i].getTime());
map.put("内容",news[i].getContent());
listItems.add(map);
}
SimpleAdapter adapter=new SimpleAdapter(this,listItems,R.layout.listview,
new String[]{"图标","标题","来源","时间","内容"},
new int[]{R.id.news_picture,R.id.news_title,R.id.news_source,R.id.news_time,R.id.news_content});
listView.setAdapter(adapter);
//判断横竖屏
Configuration mConfiguration = MainActivity.this.getResources().getConfiguration();
int ori = mConfiguration.orientation;
if (ori == mConfiguration.ORIENTATION_LANDSCAPE) {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Map<String,Object>map=(Map<String, Object>)parent.getItemAtPosition(position);
String Title=String.valueOf(map.get("标题"));
String Content= String.valueOf(map.get("内容"));
String Sourse= String.valueOf(map.get("来源"));
String Time= String.valueOf(map.get("时间"));
DetailFragment fragment=(DetailFragment)getSupportFragmentManager().findFragmentById(R.id.fragment_content);
fragment.refresh(Title,Content,Sourse,Time);
}
});
} else if (ori == mConfiguration.ORIENTATION_PORTRAIT) {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Map<String,Object>map=(Map<String, Object>)parent.getItemAtPosition(position);
String Title=String.valueOf(map.get("标题"));
String Content= String.valueOf(map.get("内容"));
String Sourse= String.valueOf(map.get("来源"));
String Time= String.valueOf(map.get("时间"));
Intent intent=new Intent();
intent.setClassName(MainActivity.this,"com.example.labfourfragment.DetailActivity");
intent.putExtra("newsTitle",Title);
intent.putExtra("newsContent",Content);
intent.putExtra("newsSourse",Sourse);
intent.putExtra("newsTime",Time);
startActivity(intent);
}
});
}
}
}
2.动态添加Fragment
先创建一个MyFragment碎片,然后在.java文件中:
无需在布局文件中添加标签
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
MyFragment fragment=new MyFragment();
FragmentManager fm=getSupportFragmentManager();//获取FragmentManager实例
FragmentTransaction beginTransaction=fm.beginTransaction();//获取FragmTransaction实例
beginTransaction.add(R.id.main2_activity,fragment);//添加一个Fragment,向布局文件main2_activity添加碎片
beginTransaction.commit();
}
}
Fragment 的返回
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
MyFragment fragment=new MyFragment();
FragmentManager fm=getSupportFragmentManager();//获取FragmentManager实例
FragmentTransaction beginTransaction=fm.beginTransaction();//获取FragmTransaction实例
// beginTransaction.add(R.id.main2_activity,fragment);//添加一个Fragment,向布局文件main2_activity添加碎片
// beginTransaction.commit();
//Fragment的返回,返回上一个碎片,直到没有碎片为止
beginTransaction.replace(R.id.main2_activity,fragment);
beginTransaction.addToBackStack(null);
beginTransaction.commit();
}
}
3.Fragment和Activity之间的通信
在碎片中获取碎片实例
得到碎片实例后,活动即可调用碎片中的方法,如:在活动中操作碎片的控件在碎片中获取活动实例:
MainActivity activity=(MainActivity)getActivity();
得到活动实例后,即可调用任何活动中的方法:
MainActivity acticity=(MianActivity)getActivity();
Textview tv=(TextView)activity.findViewById(R.id.show_text);
4.碎片的生命周期
- Fragment不能独立存在必须嵌入到Activity中使用,所以Fragment生命周期直接受所在Activity影响。
- 当Activity暂停时,它拥有的所有Fragment都暂停,当Activity销 毁时,它拥有的所有Fragment都被销毁。
五.广播(Broadcast Receiver)
为了监听来自系统或者应用程序的广播事件,Android系统提供了BroadcastReceiver(广播接收者)组件
当Android系统产生一个广播事件时,可以有多个对应的广播接收者接受并处理
广播的类型:有序广播+无序(标准)广播
有序广播:完全异步执行,发送广播时所有监听这个广播的广播接收者都会收到此消息,但是接受的顺序不确定
无序广播:按照接收者的优先级接收,只有一个广播接收者能接收消息,在此广播接收者逻辑执行完毕之后,才会继续传递
5.1创建广播接收者
com.example.review–>New–>Other–>Broadcast Receiver
其中属性Exportd属性表示是否允许这个广播接收者接收本程序以外的广播,Enabled属性表示是否启用这个广播接收者
勾选上述两个属性,点击finish完成创建
5.2静态注册广播(例子:提示开机成功)
首先在manifest中注册,注册权限use-permission和intent-filter中的action:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.review">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>
<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/AppTheme">
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<!--此广播接收者接收的特定广播类型:下面是开机自启动服务-->
<action android:name="android.intent.action.RECEIVE_BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
<activity android:name=".Main2Activity" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
5.3动态注册广播
private MyBroadcastReceiver myBroadcastReceiver;
private IntentFilter intentFilter;
...
//在onCreate方法里有:
Intent intent = new Intent("android.intent.action.RECEIVE_BOOT_COMPLETED");
intent.putExtra("MyBroadMessage","send message");
//打开另一服务
intent.setComponent(new ComponentName("com.example.broadcastreceiver","com.example.broadcastreceiver.MyBroadcastReceiver"));
sendBroadcast(intent);
//在onDestroy方法中:(必须取消注册)
unregisterReceiver(myBroadcastReceiver);
5.4自定义广播
当自定义广播发送消息时,会储存到公共消息区,而公共消息区如果存在对应的广播接收者,就会及时的接收消息
发送标准广播:
Intent intent2=new Intent();
intent2.setAction(“在注册文件中的filter的name”);
sendBroadcast(intent2);
发送有序广播
Intent intent2=new Intent();
intent2.setAction(“在注册文件中的filter的name”);
sendOrderedBroadcast(intent2,null);
首先new一个Broadcast Receiver,然后写注册文件
自定义一个Filter的名字,记住它,后面会用:
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="MyReceiver"></action>
</intent-filter>
</receiver>
书写发出广播:
Button button=findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent1=new Intent("MyReceiver");
//变量以及变量对应的值
intent1.putExtra("extraKey","CustomValue");
intent1.setComponent(new ComponentName("com.example.broadcastreceiver","com.example.broadcastreceiver.MyReceiver"));
sendBroadcast(intent1);
}
});
书写接收广播:
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("MyBroadcast")){
String msg=intent.getStringExtra("extraKey");
Log.d("MyBroadcastReceiver", "extraKey:"+msg);
}
}
5.5有序广播的优先级
在filter中增加priority属性,数值越大,优先级越高。若两者优先级相同,则先注册的广播接收者优先级高:
< intent-filter android:priority=“100”>
< action android:name=“MyReceiver”>< /action>
< /intent-filter>
在广播中:
//可截断广播,优先级低的接收器无法接收
public void onReceive(Context context, Intent intent) {
abortBroadcast();
}
5.6本地广播
androidx.localbroadcastmanager.content.LocalBroadcastManager
1.广播注册:动态注册方式
2.广播接收器定义:与全局广播一样,继承BroadcastReceiver并重写onReceiver方法
3.广播注册/注销:
使用LocalBroadcastManager下的registerReceiver方法注册
使用LocalBroadcastManager下的unRegisterReceiver方法注销
4.广播发送:
1)创建LocalBroadcastManager实例
LocalBroadcastManager lbc = LocalBroadcastManager.getInstance(this);
2)创建Intent实例,并指定广播的action
Intent intent = new Intent(“com.example.broadcast.LOCAL_BROADCAST”);
3)使用LocalBroadcastManager的sendBroadcast()方法发送
lbc. sendBroadcast(intent);
六.持久化技术(SQLite和SharedPreference)
持久化技术:将内存中的数据存入持久化存储设备中,如:硬盘
- 文件流/字节序列:使用文件流存储为文件
- 键值对:存储和传输结构简单的数据,如:单个变量或简单序列
HTTP请求数据(数据传输)、Android SharedPreferences等- 对象:面向对象编程中,内存中的数据以对象形式存在——对象持久化;对象包含所属类名、属性名、属性值等,理想的存储介质是数据库、JSON字串、XML文档等
3.1 数据库:将数据库表映射到对象的持久层框架
Java技术:Hibernate、MyBatis
微软技术:Entity Framework、Linq
3.2. JSON和XML:将对象与JSON字串/XML文档相互转换,通常用于对象的文件化存储和远程传输
JSON字串的序列化/反序列化
XML文档的序列化/反序列化
1.文件存储
文件存储是Android中最基本的一种数据存储方式,它与Java中的文件存储类似,都是通过I/O流的形式把数据存储到文档中。
文件存储分为内部存储和外部存储
内部存储
将应用程序中的数据以文件方式存储到设备的内部,当创建的应用程序被卸载时,其内部存储文件也随之被删除。
外部存储
是将文件存储到一些外部设备上,例如SD卡或者设备内嵌的存储卡,属于永久性的存储方式。
1.1内部存储的写入和读取
找书写的内部文件:
点击搜索图标(放大镜),输入File Explorer,回车,打开文件:data/data/com.example.项目的名字/files,书写的data文件就在这下面
//内部存储的写入
String fileName="data.txt";
String content="helloworld";
FileOutputStream fos;
try{
fos=openFileOutput(fileName,MODE_PRIVATE);
fos.write(content.getBytes());
fos.close();
}catch (Exception e){
e.printStackTrace();
}
内部文件的读取
//内部储存的读取
String content1="";
FileInputStream fis;
try{
fis=openFileInput("data.txt");
byte[]buffer=new byte[fis.available()];
fis.read(buffer);
content1=new String(buffer);
Log.d("Review",content1);
fis.close();
}catch (Exception e){
e.printStackTrace();
}
1.2外部文件的存入和读取
由于操作SD卡中的数据属于系统中比较关键的信息,因此需要在清单文件的< manifest>节点中添加SD卡的读写权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
外部文件的写入:
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
File SDPath = Environment.getExternalStorageDirectory();
File file = new File(SDPath, "data.txt");
String data = "HelloWorld";
FileOutputStream fos;
try {
//将数据存储到SD卡中
fos = new FileOutputStream(file);
fos.write(data.getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
外部文件的读取:
//获取外部设备判断外部设备是否可用获取SD卡目录
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
File SDPath = Environment.getExternalStorageDirectory();
File file = new File(SDPath, "data.txt");
FileInputStream fis;
try {
//获取指定文件对应的输入流,将其包装成BufferReader读取文件
fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String data = br.readLine();
} catch (Exception e) {
e.printStackTrace();
}
}
2.SharedPreference
-
SharedPreferences是Android平台上一个轻量级的存储类。
-
用于存储应用程序的配置参数,如用户名、密码等。
-
通过key/value(键值对)的形式将数据保存在XML文件中。
-
value值只能是float、int、long、boolean、String、StringSet类型数据
***例子:***使用SharedPreferences保存已看过的新闻ID,下次再进入时,以灰色显示(详情见综合例子)
存储数据:
SharedPreferences对象只能获取数据,必须通过SharedPreferences的Editor对象来操作数据
注意:
操作完数据后,一定要调用commit()方法提交数据,否则所有操作不生效。
//获取SP对象,data表示文件名,MODE_PRIVATE表示文件操作类型
SharedPreferences sp = getSharedPreferences("data",MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();//获取编辑器
editor.putString(“name”, “西南石油大学");//存入数据
editor.putInt("age", 61);//存入数据
editor.commit(); //提交修改
获取数据:
通过SharedPreferences对象的getXXX()方法来实现获取:
注意:
getXXX()方法第二个参数为缺省值,如果SharedPreferences中不存在该key,将返回缺省值。例如getString(“name”,””),若name不存在则key就返回空字符串。
SharedPreferences sp = getSharedPreferences("data",MODE_PRIVATE);
String data = sp.getString("name","");
注意事项:
- 获取数据的key值与存入数据的key值数据类型要一致,否则查找不到指定数据。
- 保存SharedPreferences的key值时,使用静态变量保存,以免操作时写错,如private final String key = “itcast”。
3.SQLite
- SQLite是一个轻量级数据库,占用资源非常低,在内存中只需要占用几百KB的存储空间。
- SQLite是遵守ACID的关系型数据库管理系统,ACID是指数据库事务正确执行的四个基本要素。原子性-A,一致性-C,隔离性-I,持久性-D
- SQLite保存数据时,支持NULL(零)、INTEGER(整数)、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)五种数据类型。
3.1数据库的创建
public class MyHelper extends SQLiteOpenHelper {
public MyHelper(Context context) {
super(context, “test.db", null, 2);
}
//第一次创建时调用,用于初始化表结构
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE information(_id INTEGER PRIMARY
KEY AUTOINCREMENT, name VARCHAR(20), price INTEGER)");
}
// 当数据库的版本号增加时调用
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
3.2添加数据
public void insert(String name,String price) {
//获取可读写对象SQLiteDatabase
SQLiteDatabase db = helper.getWritableDatabase();
//创建ContentValue对象,并将数据添加到ContentValues对象中
ContentValues values = new ContentValues();
values.put("name", name);
values.put("price", price);
//调用insert()方法将数据添加到数据库中
long id = db.insert("information",null,values);
db.close();
}
3.3修改数据
public int update(String name, String price) {
SQLiteDatabase db = helper.getWritableDatabase();
//创建ContentValue对象,并将修改的数据添加到ContentValues对象中
ContentValues values = new ContentValues();
values.put("price", price);
//调用update()方法修改数据
int number = db.update("information", values, " name =?", new String[]{name});
db.close();
return number; }
3.4删除数据
public int delete(long id){
SQLiteDatabase db = helper.getWritableDatabase();
int number = db.delete("information", "_id=?", new String[]{id+""});
db.close();
return number;
}
3.5查询数据
public boolean find(long id){
SQLiteDatabase db = helper.getReadableDatabase();
//调用query()方法查询数据库中的数据,返回一个行数集合Cursor
Cursor cursor = db.query("information", null, "_id=?", new
String[]{id+""}, null, null, null);
boolean result = cursor.moveToNext();
cursor.close();
db.close();
return result;
}
3.6另外的写法
//添加
db.execSQL(“insert into tblStu(name,age) values(?,?)”,new Object[]{值1,值2})
//修改
db.execSQL(“update tblStu set name=? and age =?)”,new Object[]{值1,值2})
//删除
db.execSQL(“delete from tblStu where num=?”,new Object[]{值})
//查询
Cursor cusor=db.rawQuery(“select * from tblStu where stuNo=?”,new String[]{值})
3.7事务
SQLiteDatabase db = helper. getWritableDatabase();
db.beginTransaction();
db.execSQL(…..)
db.execSQL(…..)
db.setTransactionSuccessful();
db.endTransaction() ;
db.close();
4.综合例子(实验报告6,SharedPreference和SQLite)
MainActivity:
public class MainActivity extends AppCompatActivity {
MyHelper myHelper=new MyHelper(MainActivity.this);
SQLiteDatabase db;
ContentValues values;
private static final String TAG="Test";
//记录新闻详情浏览时间
static long starttime=0;
static long endtime=0;
//记录新闻被点击的行号
String number;
//用于承装浏览过的新闻ID
final static List<String> lstID=new ArrayList<String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
setContentView(R.layout.activity_main);
//向数据库中录入数据
News news;
news=new News(0,"title","content","source","time");
insert(news.getnID(),news.getTitle(),news.getContent(),news.getSource(),news.getTime());
//初始化新闻列表
db=myHelper.getReadableDatabase();
final ListView listView=findViewById(R.id.listview);
final List<Map<String,String>> listItems=new ArrayList<Map<String,String>>();
//查询数据库中数据,将数据库数据填入新闻列表
Cursor cursor=db.query("NewsTable",null,null,null,null,null,null);
while(cursor.moveToNext()){
Map<String,String>map=new HashMap<>();
map.put("ID",cursor.getString(cursor.getColumnIndex("nID")));
map.put("标题",cursor.getString(cursor.getColumnIndex("Title")));
map.put("内容",cursor.getString(cursor.getColumnIndex("Content")));
map.put("来源",cursor.getString(cursor.getColumnIndex("Source")));
map.put("时间",cursor.getString(cursor.getColumnIndex("time")));
listItems.add(map);
}
//创建适配器
SimpleAdapter adapter=new SimpleAdapter(this,listItems,R.layout.items,
new String[]{"标题","来源","时间","内容"},
new int[]{R.id.news_title,R.id.news_source,R.id.news_time,R.id.news_content});
//设置适配器
listView.setAdapter(adapter);
//判断横竖屏:
Configuration mConfiguration = MainActivity.this.getResources().getConfiguration();
int ori=mConfiguration.orientation;
if(ori==mConfiguration.ORIENTATION_LANDSCAPE){//横屏时:
//点击事件:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Map<String,String>map=(Map<String, String>)parent.getItemAtPosition(position);
//获取点击位置的ID
number=String.valueOf(position);
String Title=map.get("标题");
String Content= map.get("内容");
String Sourse= map.get("来源");
String Time= map.get("时间");
DetailFragment fragment=(DetailFragment)getSupportFragmentManager().findFragmentById(R.id.fragment_content);
fragment.refresh(Title,Content,Sourse,Time);
}
});
}
else if(ori==mConfiguration.ORIENTATION_PORTRAIT){//竖屏时:
//点击事件:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Map<String,String>map=(Map<String, String>)parent.getItemAtPosition(position);
//获取点击位置的ID
number=String.valueOf(position);
String Title=map.get("标题");
String Content= map.get("内容");
String Sourse= map.get("来源");
String Time= map.get("时间");
Intent intent=new Intent();
intent.setClassName(MainActivity.this,"com.example.labsixsqlite.DetailActivity");
intent.putExtra("newsTitle",Title);
intent.putExtra("newsContent",Content);
intent.putExtra("newsSourse",Sourse);
intent.putExtra("newsTime",Time);
startActivity(intent);
}
});
}
}
//向数据库中增加数据
public void insert(int nID,String Title,String Content,String Source,String time){
//获取可读写SQLiteDatabase对象
db = myHelper.getReadableDatabase();
values=new ContentValues();
for(int i=0;i<6;i++){
values.put("nID",nID+i);
values.put("Title",Title+i);
values.put("Content",Content+i);
values.put("Source",Source+i);
values.put("time",time+i);
//调用Inset方法将数据添加到数据库中
db.insert("NewsTable",null,values);
values.clear();
}
}
@Override
protected void onRestart() {
super.onRestart();
endtime=System.currentTimeMillis();
getALRead();
changeClolor();
}
@Override
protected void onDestroy() {//退出应用时,关闭数据库连接
super.onDestroy();
if(db!=null){
db.close();
}
}
private void getALRead()
{
//达到规定阅读时间,将新闻ID存入文件
if((endtime-starttime)/1000>3){
Log.d(TAG,"duration:"+(endtime-starttime)/1000);
SharedPreferences.Editor editor=getSharedPreferences("AlRead",MODE_PRIVATE).edit();
lstID.add(number);
Log.d(TAG,"存入lstID中的新闻ID:"+number);
//将已读新闻ID转化为字符串保存
String str="";
for(int i=0;i<lstID.size();i++){
str+=lstID.get(i)+",";
}
editor.putString("ID",str);
Log.d(TAG,"达到阅读时间的所有新闻ID:"+str);
editor.apply();
}
}
private void changeClolor(){
File alRead=new File("data/data/com.example.labsixsqlite/shared_prefs","AlRead.xml");
if(alRead.exists()){
SharedPreferences pref=getSharedPreferences("AlRead",MODE_PRIVATE);
String str=pref.getString("ID","");
//将字符串以","分割为字符数组sstr
String[] sstr=str.split(",");
//将lstAlRead中的新闻ID
for(int i=0;i<sstr.length;i++){
//获取item的layout的index
ListView listView= findViewById(R.id.listview);
LinearLayout layout=(LinearLayout)listView.getChildAt(Integer.parseInt(sstr[i]));
TextView textView=layout.findViewById(R.id.news_title);
textView.setTextColor(Color.RED);
}
}
}
}
public class DetailFragment extends Fragment {
String TAG="DetailFragment";
long starttime=0;
public DetailFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG,"onCreateView");
View view=inflater.inflate(R.layout.fragment_detail,container,false);
return view;
}
public void refresh(String news_title, Object news_content, Object news_sourse, Object news_time){
View v=getView().findViewById(R.id.fragment_content);
v.setVisibility(View.VISIBLE);
TextView news_Title= v.findViewById(R.id.news_title);
TextView news_Content= v.findViewById(R.id.news_content);
TextView news_Sourse= v.findViewById(R.id.news_source);
TextView news_Time= v.findViewById(R.id.news_time);
news_Title.setText(news_title);
news_Content.setText((CharSequence) news_content);
news_Sourse.setText((CharSequence) news_sourse);
news_Time.setText((CharSequence) news_time);
}
@Override
public void onStart() {
super.onStart();
starttime=System.currentTimeMillis();
MainActivity.starttime=starttime;
Log.d(TAG,"onStart");
}
}
public class DetailActivity extends AppCompatActivity {
long starttime=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
//获取数据
Intent intent=getIntent();
TextView DetailNewsContent=findViewById(R.id.news_content);
TextView DetailNewsTitle=findViewById(R.id.news_title);
TextView DetailNewsSource=findViewById(R.id.news_source);
TextView DetailNewsTime=findViewById(R.id.news_time);
DetailNewsContent.setText(intent.getStringExtra("newsContent"));
DetailNewsTitle.setText(intent.getStringExtra("newsTitle"));
DetailNewsSource.setText(intent.getStringExtra("newsSourse"));
DetailNewsTime.setText(intent.getStringExtra("newsTime"));
}
@Override
protected void onStart() {
super.onStart();
starttime=System.currentTimeMillis();
MainActivity.starttime=starttime;
}
}
public class MyHelper extends SQLiteOpenHelper {
public MyHelper(Context context){ super(context,"NewsTable.db",null,2); }
@Override
//第一次创建时调用,用于初始化表结构
//创建新闻数据库,包含新闻表
//第一次加载时能创建数据库及表
public void onCreate(SQLiteDatabase db) {
db.execSQL("Create table NewsTable(" +
"nID integer primary key autoincrement," +
"Title text," +
"Content text," +
"Source text," +
"time text)");
}
@Override
//当数据库的版本号增加时调用
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
七.跨程序共享数据-内容提供器(Content Provider)
Android系统四大组件:Activity+Service+BroadCast+Content Provider
- 内容提供者(ContentProvider)是Android系统四大组件之一,它是不同应用程序之间进行数据共享的标准API,通过ContentResolver类可以访问ContentProvider中共享的数据。
工作原理:
- ContentResolver
- ContentResolver提供一系列增删改查的方法对数据进行操作,并且这些方法以Uri的形式对外提供数据。
- Uri为内容提供者中的数据建立了唯一标识符。它主要由三部分组成,scheme、authorities和path。
1.运行时权限(运行时授权)
-
数据共享涉及到权限问题
-
权限种类:
一般权限:不会威胁隐私的权限
危险权限:威胁到隐私的权限,仅对此类权限进行运行时授权
特殊权限:很少使用
- Android管理权限的方式是通过权限注册
在AndroidManifest.xml中注册,这样在APP安装时会提示用户权限
< use-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-
同时为了防止一些刚需软件在安装时提示权限过多,导致用户无法拒绝的现象出现,Android在6.0以后增加了运行时权限功能
-
运行时权限
用户不需要在安装时一次性授权所有权限,而是在用到该功能时选择是否授权,这样就使得用户可以在拒绝一部分功能后,仍然可以使用APP的其他功能
运行时授权仅针对危险权限
1.1拨打电话例子:
6.0版本以下:
注册权限–>编写代码
<use-permission android:name="android.permission.CALL_PHONE" />
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
6.0以上版本
判断是否已运行时授权:
//检查CALL_PHONE权限是否曾被授权
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED)
{
//打开请求授权框,用户可以看到一个CALL_PHONE的请求授权对话框;
//可在框中选择授权、拒绝,或直接关闭对话框
//1是请求码
ActivityCompat.requestPermissions(MainActivity.this, new
String[]{Manifest.permission.CALL_PHONE}, 1);
}
else{
call(); //如果有权限,则执行call()方法
}
是否接受运行时授权
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults){
switch(requestCode){
case 1:
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
call();
}
else{
Toast.makeText(MainActivity.this, "You denied the permission", Toast.LENGTH_LONG).show();
}
break;
default:
}
}
执行动作:
public void call(){
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
2.访问其他程序数据
访问内容提供者:
//获取相应操作的Uri,Uri.Parse()方法将字符串转化成Uri对象
Uri uri = Uri.parse("content://cn.itcast.mycontentprovider/person");
//获取ContentResolver对象
ContentResolver resolver = context.getContentResolver();
//通过ContentResolver对象查询数据
Cursor cursor = resolver.query(uri, new String[] { "address", "date","type", "body" },
null, null, null);
while (cursor.moveToNext()) {
String address = cursor.getString(0);
long date = cursor.getLong(1);
int type = cursor.getInt(2);
String body = cursor.getString(3);
}
cursor.close();
3.内容观察者的使用
创建内容提供者步骤:
-
在程序包名处点击右键选择【New】–>【Other】–>【Content Provider】选项
-
输入内容提供者的Class Name(名称)和URI Authorities(唯一标识,通常使用包名)
-
点击【Finish】按钮创建完成
创建完成后,Android Studio会在注册文件中对内容提供者进行注册
3.1内容提供者概述
内容提供者封装在ContentProvider中,该类是一个抽象类,包含了6个抽象方法,必须全部重写
- onCreate():内容提供器初始化时调用,通常用于数据库的创建、升级等操作。
- query():查询数据
- insert():插入数据
- update():修改数据
- delete():删除数据
- getType():根据URI返回MIME类型
- MIME用于在网络传输中定义资源类型
3.2内容提供者Uri
-
内容访问者(ContentResolver)通过URI访问内容提供器(ContentProvider)
-
格式:content://包名/表名/id,如:content://com.example.provider/table1/1
-
通配符:表名和id都可用通配符表达
-
*:表示任意长度的任意字符
-
#:表示任意长度的数字
-
如:content://com.example.provider/* 匹配任意表的内容
-
content://com.example.provider/table1/# 匹配table1表的任意行数据