精华内容
下载资源
问答
  • Android四大组件系列

    2019-04-03 15:42:32
    Android四大组件之Activity Android四大组件之Service Android四大组件之BroadcastReceiver Android四大组件之ContentProvider

    Android四大组件之Activity
    用于表现功能

    Android四大组件之Service
    后台运行服务,不提供界面呈现

    Android四大组件之BroadcastReceiver
    用于发送、接收广播

    Android四大组件之ContentProvider
    支持多个应用中存储和读取数据,相当于数据库

    展开全文
  • Android 四大组件系列详解: Android 四大组件之—Activity 详解 Android 四大组件系列之—-... Service是Android四大组件之一,Service通常总是称之为“后台服务”,它与Activity很相似,都从Context派生出来...

    Android 四大组件系列详解:
    Android 四大组件之—Activity 详解
    Android 四大组件系列之—-Service 详解
    Android 四大组件系列之—BroadcastReceiver 详解

    一. Service简介

    Service是Android四大组件之一,Service通常总是称之为“后台服务”,它与Activity很相似,都从Context派生出来,都有属于自己的生命周期,但Service与Activity又有很大的不同,Activity呈现一个供用户操作的界面,是直接与用户交互的组件;而Service是没有用户界面的,是一个可以在后台执行长时间运行操作而不使用用户界面的应用组件,Service可以在后台执行网络请求、播放音乐、O文件操作,或者使用它开发如监控之类的后台程序,如,检测SD卡上文件的变化,实时监控地理信息位置的改变等;

    二. Service 的生命周期

    1. Service生命周期

    (1) onCreate

    当Service第一次被创建时回调该方法,onCreate() 该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。

    (2) onStartCommand

    客户端每次调用startService方法启动Service时回调该方法;

    (3) onBind(Intent intent)

    Service子类必须实现该方法,该方法返回一个IBinder对象,客户端可以通过该方法与Service进行进程间通信;

    (4) onUnbind(Intent intent)

    当Service与之前绑定的所有客户端都断开连接时回调该方法;

    (5) onDestory

    当Service关闭之前回调该方法;

    下图是两种启动方式下Service的生命周期:
    这里写图片描述

    2. 创建,配置Service

    3. 客户端启动 Service 的两种方式

    Service 需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,区别如下:

    startService()方法启动

    调用者与 Service 之间没有关连,即使调用者退出了,服务仍然继续运行
    使用Context.startService() 方法启动服务,在服务未被创建时,系统会先调用服务的 onCreate() 方法,接着调用 onStart() 方法。

    如果调用 startService() 方法前服务已经被创建,多次调用 startService() 方法并不会导致多次创建服务,但会导致多次调用onStart()方法。

    使用 startService() 方法启动的服务,只能调用 Context.stopService() 方法结束服务,服务结束时会调用onDestroy()方法。

    只有使用 Context.startService() 方法启动服务时才会回调 onStart() 方法。该方法在服务开始运行时被调用。

    bindService()方法启动

    调用者与 Service 绑定在了一起,调用者一旦退出,Service 也就跟着终止,通过 bindService(Intent service, ServiceConnection conn, int flags)跟 Service 进行绑定,当绑定成功的时候 Service 会将代理对象通过回调的形式传给 conn,这样就拿到了 Service 提供的服务代理对象, 从而可以调用service 提供的一些接口.

    使用 Context.bindService() 方法启动服务,在服务未被创建时,会先调用服务的 onCreate() 方法,接着调用 onBind() 方法。这个时候调用者和服务绑定在一起了; 如果此时调用者退出了,系统就会先调用服务的onUnbind() 方法,接着调用 onDestroy() 方法,导致服务也退出.

    如果调用 bindService() 方法前服务已经被绑定,多次调用 bindService() 方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。

    如果调用者要与正在绑定的服务解除绑定,可以调用 unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()方法, 接着调用onDestroy()方法。

    只有使用Context.bindService()方法启动服务时才会回调onBind()方法, 当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法onBind()被多次调用。

    只使用Context.bindService()方法启动服务时才会回调 onUnbind() 方法。该方法在调用者与服务解除绑定时被调用

    2. 需要特别注意的点

    1. Service运行在主线程中, 不能在service中做耗时操作

    需要注意的一点是,由于Service运行在后台,字面上很容易让人误以为Service运行在一个单独的线程里,这时错误的; 其实Service运行在主线程中,Service并不是一个新的线程,更不是一个新的进程,UI主线程是不能执行耗时操作的,否则会出现卡顿,甚至ANR(Application not response),因此,在Service中不能执行太耗时的操作,否则将会阻塞主线程,如果需要执行耗时操作应该在Service中创建一个新的线程.

    service也可以运行在单独的进程中, 在清单AndroidManifest.xml 文件配置 service 执行所在的进程 ,让 service 在新的进程中运行:

      <service
         android:name="com.cxh.serviceTest.service"
         android:enabled="true"
         android:process=":remote" >
      </service>

    3. Service 和 Thread 区别

    Service 给人的感觉就是一个没有用户界面的线程一样, 如果是这样, 那岂不是和 Thread 作用一样了, 并且service 使用比 Thread 复杂的多, 是不是Android 多此一举了呢?

    Service四大组件之一,它运行在独立进程的UI主线程中,不可以执行耗时操作
    Thread是程序执行的最小单元,分配CPU的基本单位,可以开启子线程执行耗时操作; Service在不同Activity中可以获取自身实例,可以方便的对Service进行操作,Thread几乎在任何地方可以使用, 如果在Activity中被创建和启动, 当Activity被销毁,Thread实例就很难再获取到;

    什么时候使用service, 要回答这个问题我们必须要知道 Android 应用进程的优先级:

    Android 官方文档进程根据优先级的由高到低分为以下五种:
    前台进程: 屏幕正在展示的界面,并且正在与用户交互的,其实任何进程,如果它持有前台activity的服务,那这个进程也有了前台优先级的;
    可见进程: 就是能看到的,比如A Activity启动了B Activity,但B Activity是透明或者没有完全覆盖住A Activity,此时A Activity就处于可见进程;
    注:给Service 设置 setForground(true) 可以使服务类似于前台进程,可尽量保证服务不会被回收;
    服务进程: 当一个进程中有服务(Service)运行时,该进程属于服务进程;
    后台进程:按下Home键后Activity在后台运行,该进程就处于后台进程, 当系统内存不足时,这种进程很可能会被回收;
    空进程: 就是按back键退出以后的进程,这种进程一般是为了缓存一些数据,当系统内存不足时最先回收空进程。
    当系统内存不足的时候,低优先越低的进程越容易被系统回收掉,相反优先级越高的进程,不容易被回收;

    如上所述可以知道Service(服务进程)的优先级高于后台的Activity,如果Activity中创建并启动了 Thread,那Service优先级肯定也高于的Thread,因此系统可能在内存不足的时会回收后台的Activity和Thread,但不会轻易回收掉Service;
     
    还有一个问题就是 Activity 中创建的 Thread 如果在 Activity退出时没有将该Thread停止释放掉,那么该Thread将继续在后台运行,会变的不可控,如果系统内存充足它将长时间占用内存。

    展开全文
  • final void attach(Context context) { attachBaseContext(context); mLoadedApk = ContextImpl.getImpl(context).mPackageInfo; }
  • Android 四大组件,五大存储,六大布局

    万次阅读 多人点赞 2018-08-15 12:28:24
    android四大组件分别是:Activity, service,content provider,broadcast receiver Activity Service Content Provider Broadcast Receiver Android 五大存储 SharedPreferences 方式 ...

    Android 四大组件

    android四大组件分别是:Activity, service,content provider,broadcast receiver

    一、Activity

    1、概念:

    android 中,Activity 相当于一个页面,可以在Activity中添加Button、CheckBox 等控件,一个android 程序有多个Activity组成。

    2、生命周期:

    这里写图片描述

    3、四中启动模式

    Standard 模式 : standard 模式是android 的默认启动模式,在这种模式下,activity可以有多个实例,每次启动Activity,无论任务栈中是否已经存在这个activity的实例,系统都会创建一个新的activity实例。

    SingleTop 模式: 栈顶模式,当一个singleTop模式的activity 已经位于栈顶时,再去启动它时,不在创建实例,如果不在栈顶,就会创建实例。

    SingleTask 模式 : 单任务模式,如果启动的activity 已经存在于 任务栈中,则会将activity移动到栈顶,并将上面的activity出栈,否则创建新的实例

    SingleInstance 模式 :单实例模式,一个activity 一个栈。

    4、三种跳转方式

    显示启动
    Intrent 内部直接声明要启动的activity所对应的的class

    Intent intent = new Intent(MainActivity.this, SecondActivity.class);
    startActivity(intnet);
    

    隐式启动
    进行三个匹配,一个是activity,一个是category,一个是data,全部或者部分匹配,应用于广播原理

    • 清单文件中 里配置activity属性,activity的名字要和跳转内容一样
    <activity 
    	android:name="com.exanple.android.tst.secondActivity"
    	android:label = @string/title>
    	<intent=filter>
    		<action android:name="com.exanple.android.tst.secondActivity/>
    		<category android:name="android.intent.category.DEFAULT"/>
    	<intent-filter/>
    </activity>
    
    • 在需要跳转的地方
    Intent intent = new Intent("com.example.android.tst.secondActivity");
    startActivity(intnet);
    

    跳转后再返回,能获取返回值

    Intent in = new Intent(MainActivity.this,OtehrActivity.class);
    in.putExtra("a",a);
    startActivityForResult(in,1000);
    

    在OTherActivity中设置返回值

    Intent int = new Intent();
    int.putExtra("c",c);
    setResult(1001,int);
    finish();
    

    在MainActivity中获取返回值

    @Override
    protected void onActivityResult(int requestCode, int resultCode	,Intent data) {
    	super.onActivityResult(requestCode,resultCode,data);
    	if(requestCode == 1000){
    		if(resultCode == 1001){
    			int c = data.getExtra("c",0);
    		}
    	}
    }
    

    Service

    定义一个Server

    项目内Server包 右键 --> New --> Service --> Service 或者直接创建Class类,继承Service并重写IBinder方法

    public class MyService extends Service{
    	
    	public MyService(){
    		
    	}
    
    	@Override
    	public IBinder onBind(Intent intent) {
    		return null;
    	}
    	
    	@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() {
    		// TODO Auto-generated method stub
    		super.onDestroy();
    	}
    }
    

    重写Service的 onCreate()、onStartCommand()和onDestory()方法。其中 onCreate() 方法在服务创建的时候调用、onStartCommand() 方法会在每次服务启动的时候调用、onDestory() 方法会在服务销毁的时候调用。
    通常情况下,如果我们希望服务一旦启动就立刻去执行任务,就可以将逻辑卸载onStartCommand() 方法里。
    另外需要注意的是,每个服务都需要在Androidmanifest.xml 中进行注册才能生效:

    <application
    	....>
    	...
    	<service
    		android:name=".MyService"
    		android:enabled="true"
    		android:exported="true">
    	</service>
    </application>
    
    启动和停止服务

    启动服务:

    Intent startIntent = new Intent(this, MyService.class);
    startService(startIntent); //启动服务
    

    停止服务:

    Intent stopIntent = new Intent(this, MyService.class);
    stopService(stopIntent); //停止服务
    
    使用前台服务

    前台服务与普通服务的最大区别在于,它会一直有一个正在运行的图标在系统的状态栏中,下拉状态栏后可以看到更加详细的内容,非常类似于通知的效果。

    public class MyService extends Service{
    	Intent intent = new Intent(this, MainActivity.class);
    	PendingIntent pi = PendingIntent.getActivity(this, 0 , intent, 0);
    	Notification notification  = new NotificationCompat.Builder(this)
    		.setContentTitle(" this is content titile")
    		.setContentText("this is content text")
    		.setWhen(System.currentTimeMillis())
    		.setSmallIcon(R.mipmap.ic_launcher);
    		.setLargeIcon(BitmapFactory.decodeResource(getResource(),
    			R.mipmap.ic_launcher))
    		.setContentIntent(pi)
    		.build();
    	startForeground(1,notification);
    }
    

    构造一个Notification对象后并没有使用NotificationManager 来讲通知显示出来,而是调用了startForeground()方法,该方法会将MyService变成一个前台服务,并在系统状态栏中显示出来。

    使用IntentService

    服务中的代码都默认运行在主线程中,如果直接在服务中执行耗时操作很容易出现ANR(Application not Responding)
    所以这个时候需要用到Android多线程编程技术,我们应该在服务的每个具体的方法里启动一个子线程,然后在这里去处理那些耗时的操作:

    public class MyService extends Service{
    	...
    	@Override
    	public int onStartCommand(Intent intent , int flags, int startId){
    		new Thread(new Runnable(){
    			public void run(){
    				//处理具体的逻辑
    			}
    		}).start();
    		return super.onStartCommand(intent, flags, startId);
    	}
    }
    

    但是,这种服务一旦启动之后,就会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来,所以,如果想要实现让一个服务在执行完毕后自动停止的功能,就可以这样写:

    public class MySerivce extends Servcie{
    	...
    	@Override
    	public int onStartCommand(Intent intent, int flats , int startId){
    		new Thread(new Runnable(){
    			public void run(){
    				//处理具体的逻辑
    				stopSelf();
    			}
    		});
    	}
    }
    

    虽说这样的写法并不复杂,但是总会有一些程序员忘记开启线程或者忘记调用stopSelf() 方法。为了简单创建一个异步、会自动停止的服务。Android专门提供了一个IntentService类

    public class MyIntentService extends IntentService{
    	public MyIntentService(){
    		super("MyIntentService");  //调用父类的有参构造方法
    	}
    	@Override
    	protected void onHandleIntent(Intent intent){	
    		//打印当前的线程ID
    		Log.e("mylog","Thread id is” + Thread.cuttentThread().getId();
    	}
    	@Override
    	public void onDestory(){
    		super.onDestory();
    		Log.e("mylog","on Destory executed");
    	}
    }
    

    首先这里提供一个无参的构造方法,并且必须在其内部调用父类的有参构造方法。然后要在子类中去实现onHandleIntent() 这个抽象方法,在这个方法中可以去处理一些逻辑,而且不用担心ANR,因为这个方法已经是在子线程中运行了。
    IntentService线程的调用:

    Intent intent = new Intent(this, MyIntentService.class);
    startServcie(intent);
    

    如此,线程就会自动启动并执行逻辑,执行完毕后自动关闭。这就是IntentService 的好处,能够自动开启和关闭;

    Content Provider

    对于每一个应用程序来说,如果想要访问内容提供器中共享的数据,就一定要借助ContentResolver 类,可以通过Context中的getContentResolver() 方法获取该类的实例。ContentResolver中提供了一系列的方法用于对数据进行CRUD操作,其中insert() 方法用于添加数据,update() 方法用于更新数据,delete() 方法用于删除数据,query() 方法用于查询数据。
    不同于SQLiteDatabase,ContentResolver 中的增删改查都是接收一个URl参数,这个参数被称为内容URL。内容URL给内容提供器中的数据建立了唯一标识符,它主要由两部分组成:authoritypathauthority 是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式进行命名。path则是用于对同一应用程序中不同的表做区分,通常都会添加到authority后面:

    content://com.example.app.provider/table1
    content://com.example.app.provider/table2
    

    在使用内容URL作为参数的时候,需要将URL转换成URL对象:

    Uri uri = Uri.parse("content://com.example.app.provider/table1")
    

    现在我们就可以使用这个uri对象来查询talbe1表中的数据了:

    Cursor cursor = getContentResolver().query(
    	uri,
    	projection,
    	selection,
    	selectionArgs,
    	sortOrder
    );
    

    对应参数的解释:

    query()方法参数 对应SQL部分 描述
    uri from table_name 指定查询某个应用程序下的某个表
    projection select column1, column2 指定查询的列名
    selection where column=value 指定where约束条件
    selectArgs - 为where中的占位符提供具体的值
    orderBy order by column1, column2 指定查询结果的排序方式

    查询完之后,就可以从游标中取值了:

    if(cursor != null){
    	while(cursor.moveToNext()) {
    		String column1 = cursor.getString(cursor.getColumnIndex("column1"));
    		int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
    	}
    	cursor.close();
    }
    
    增删改查

    添加数据

    ContentValues values = new ContentValues();
    values.put(“column1”, "text");
    values.put("column2", 1);
    getContentResolver().insert(uri, values);
    

    更新数据

    ContentValues valuse = new ContentValues();
    valuse.put("column1", "");
    getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[]{"text", 1});
    

    删除数据

    getContentResolver().delete(uri , "column2 = ?", new String[]{ "1"});
    
    实例.

    读取系统联系人
    读取系统联系人需要声明权限,如果系统是6.0以后的,需要申请运行时权限

    if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) 
    	!= PackageManager.PERMISSION_GRANTED) {
    		ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
    	}else {
    		readContacts();  //读取联系人
    	}
    private void readContacts(){
    	Cursor cursor = null;
    	try{
    		//查询联系人数据
    		cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
    		if(cursor!=null){
    			while(cursor.moveToNext()){
    				//获取联系人姓名
    				String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
    				//获取联系人电话号码
    				String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
    				list.add(name+"\n"+number);
    			}
    		}
    	}catch(Exception e){
    		e.printStackTrace()
    	}finally{
    		if(cursor != null){
    			cursor.close();
    		}
    	}
    }
    
    @Override
    public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults){
    	switch(requestCode){
    		case 1:
    			if(grantResults.length >0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
    				readContacts();
    			}else {
    				//您拒绝了权限
    			}
    	}
    }
    
    创建自己的内容提供器

    创建自己的内容提供器,需要去继承 ContentProvider 类,ContentProvider 类中有6个抽象方法,我们在使用子类继承它的时候,需要将这6个方法全部重写。

    public class MyProvider extends ContentProvider{
    	@Override
    	public boolean onCreate() {
    		return false;
    	}
    	@Override
    	public Cursor query(Uri uri, String[] projection, Stirng selection, String[] selectionArgs, String sortOrder){
    		return null;
    	}
    	@Overrride
    	public Uri insert(Uri uri , ContentValues values){
    		return null;
    	}
    	@Override
    	public int update(Uri uri, ContentValuse values, String selection, String[] selectionArgs){
    		return 0;
    	}
    	@Override
    	public int delete(Uri uri, String selection, String[] selectionArgs){
    		return 0;
    	}
    	@Override
    	public String getType(Uri uri){
    		return null;
    	}
    }
    

    URI 的主要格式有以下两种

    content://com.example.app.provider/table1
    content://com.example.app.provider/table1/1
    
    * : 表示匹配任意长度的任意字符
    # : 表示匹配任意长度的数字
    
    //一个能够匹配任意表的内容URI格式就可以写成:
    content://com.example.app.provider/*
    //一个能够匹配表中任意一行数据的内容URI格式就可以写成:
    content://com.example.app.provider/table1/#
    

    Broadcast Receiver

    android 广播分为两个角色:广播发送者、广播接收者
    android 广播:
    1),用于不同组件间的通信(含:应用内/不同应用之间)
    2),用于多线程通信
    3),与android系统的通信

    自定义广播接收者
    • 继承BroadcastReceive 基类
    • 必须重写抽象方法onReceive()方法
    1,广播接收器收到相应广播后,会自动调用onReceive() 方法
    2,一般情况下,onReceive方法会会涉及与其他组件之间的交互,如 发送Notiotification,启动server等
    3,默认情况下,广播接收器运行在UI线程,因此,onReceive方法不能执行耗时操作,否则将导致ANR
    
    广播接收器注册

    注册的方式有两种:静态注册、动态注册
    静态注册

    • 注册方式:在AndroidManifest.xml 里通过<receive 标签声明
    • 属性说明
    <receiver
    	android:enable="true"/"false"
    	//此broadcastReceiver 是否接受其他应用发出的广播
    	//默认值时由receiver 中d有无inter-filter决定,如果有,默认true,否则默认false
    	android:exported="true"/"false"
    	android:icon="drawable resource"
    	android:label="string resource"
    	//继承BroadcastReceiver子类的类名
        android:name=".mBroadcastReceiver"
    //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
        android:permission="string"
    //BroadcastReceiver运行所处的进程
    //默认为app的进程,可以指定独立的进程
    //注:Android四大基本组件都可以通过此属性指定自己的独立进程
        android:process="string" >
    
    //用于指定此广播接收器将接收的广播类型
    //本示例中给出的是用于接收网络状态改变时发出的广播
     <intent-filter>
    	<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
     </intent-filter>
     </receiver>
    

    注册示例:

    <receiver 
        //此广播接收者类是mBroadcastReceiver
        android:name=".mBroadcastReceiver" >
        //用于接收网络状态改变时发出的广播
        <intent-filter>
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
    </receiver>
    

    当此APP首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。

    动态注册

    • 注册方式:在代码中调用Context.registerReceiver() 方法
    • 具体代码如下:
    	// 1. 实例化BroadcastReceiver子类 &  IntentFilter
         mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
         IntentFilter intentFilter = new IntentFilter();
    
        // 2. 设置接收广播的类型
        intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
    
        // 3. 动态注册:调用Context的registerReceiver()方法
         registerReceiver(mBroadcastReceiver, intentFilter);
    
    
    //动态注册广播后,需要在相应位置记得销毁广播
    unregisterReceiver(mBroadcastReceiver);
    

    特别注意
    动态广播最好在onResume中注册, onPause注销
    原因:
    1,对于动态广播,有注册必然得有注销,否则会导致内存泄漏
    2,onPause在App死亡前一定会被执行,从而保证app死亡前一定会被注销,从而防止内存泄漏

    两种注册方式的区别

    在这里插入图片描述

    广播的发送

    广播的发送 = 广播发送者 将此广播的意图(intent)通过 sendBroasdcast() 方法发送出去
    广播的类型

    • 普通广播 系统广播 有序广播 粘性广播 App 应用内广播
    特别注意:

    对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:

    • 对于静态注册(全局+应用内广播),回调onReceive(context,
      intent)中的context返回值是:ReceiverRestrictedContext;
    • 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity
      Context;
    • 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context,
      intent)中的context返回值是:Application Context。
    • 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context,
      intent)中的context返回值是:Activity Context;

    Android 五大存储

    SharedPreferences 方式

    SharedPreferences 是使用键值对的方式进行存储数据的。
    想要使用SharedPreferences 来存储数据,首先主要获取到SharedPreferences 对象。Android提供了三种方法用于获取SharedPreferences对象:
    1,Context类中的getSharedPreferences()方法

    //此方法接收两个参数,一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data/<package name>/shared_prefs/目录下
    //第二个参数用于指定操作模式,目前只有MODE_PRIVATE这种模式,和直接传入0效果相同
    SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
    editor.putString("name", "Tom");
    editor.putInt("age",13);
    editor.putBoolean("married",false);
    editor.apply();
    

    2,Activity类中的getPreferences()方法

    //这个方法和Context中的getSharedPreferences()方法很类似,不过它只接收一个操作模式,因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名
    

    3,PreferencesManager类中的getDefaultSharedPreferences()方法

    //这是一个静态方法,它接收一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件
    

    得到了SharedPreferences对象后, 就可以开始想SharedPreferences文件中存储数据了,主要可以分为三步:
    (1)调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象
    (2)向SharedPreferences.Editor 对象中添加数据,比如添加一个布尔值,可以使用putBoolean() 方法
    (3)调用apply()方法的添加的数据提交,从而完成数据存储操作

    SharedPreferences中读取数据
    SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE	);
    String name = pref.getString("name","");
    int age = pref.getInt("age",0);
    boolean married = pref.getBoolean("married", false);
    

    文件存储方式

    SQList 存储方式

    Android 为了让我们能够更加方便的管理数据库,专门提供了一个SQLiteOpenHelper 帮助类,借助这个类可以非常简单的将数据库进行创建好升级。
    SQLiteOpenHelper 中有两个非常重要的实例方法,getReadableDatabase()getWritableDatabase() 。这两个方法可以创建或者打开一个现有的数据库(如果数据库存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入(如磁盘空间已满),getReadableDatabase方法返回的对象将以只读的方式打开数据库,而getWeitableDatabase则出现异常

    例子(在指定路径下创建数据库文件 .db )

    public class MainActivity extends Activity {
    	public static final String PATH_ONE = "KogBill";
    	public static final String PATH_NAME = "KogBill.db";
    	private SQLiteDatabase db;    //声明SQLiteDatabase ,该对象可以操作数据库
    
    	String path = Environment.getExternalStorageDirectory().getAbsolutePath();
    	String path1 = path + File.separator + PATH_ONE;   //需要创建的路径
    	String path2 = path + File.separator + PATH_ONE + 
    			File.separator + PATH_NAME;  							//需要创建的文件
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState){
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		File f = new File(path1);
    		if( !f.exists()){  //创建数据库文件路径
    			f.mkdirs();
    		}	
    		//实例化MySQLiteHelper ,创建指定目录下数据库文件,并创建表
    		MySQLiteHelper mSQL = new MySQLiteHelper(MainActivity.this, path2);
    		db = mSQL.getWritableDatabase();
    	}
    
    	class MySQLiteHelper extends SQLiteOpenHelper{
    			private static final int DATABASE_VERSION = 1;//数据库版本号
    			private static final String CREATE_TABLE = "create table kog_bill ("
    		            + "_id integer primary key autoincrement,"
    		            + "date text, "
    		            + "breakfast text, "
    		            + "lunch text,"
    		            + "dinner text,"
    		            + "happy text,"
    		            + "other text,"
    		            + "spare text)";
    			
    			//方便创建实例,简化构造方法,方法内调用4参数构造方法
    			//参数 name 可以是 数据库名称,也可以数据库文件路径(即可以指定数据库文件路径)
    			public MySQLiteHelper(Context context, String name) {
    				this(context, name, null, DATABASE_VERSION);
    			}
    			//必须要实现的方法
    			public MySQLiteHelper(Context context, String name, CursorFactory factory, int version) {
    				super(context, name, factory, version);
    			}
    	
    			@Override
    			public void onCreate(SQLiteDatabase db) {
    				// 第一次创建数据库时 才会调用
    				Log.e("mylog", "创建数据库表");
    				db.execSQL(CREATE_TABLE);
    			}
    	
    			@Override
    			public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    			}
    			
    		}
    }
    

    根据上述代码,便获得db对象,通过db(SQLiteDatabase)可进行数据库的操作,如 db.query() db.delete()

    如果我们想在创建一个数据库表,参照上述代码,可以在SQLiteOpenHelper的onCreate方法中加入语句:

    @Override
    public void onCreate(SQLiteDatebase db) {
    	db.execSQL(CREATE_TABLE);
    	db.execSQL(CREATE_BOOK);  //新创建一个数据库表
    }
    

    然后重新运行一下,发现并没有创建成功,因为KogBill.db数据库已经存在,所以MySQLiteHelper 中的onCreate方法都不会执行,解决这个办法的方法很简单,只需要将db文件删除,重新运行,便可成功,但是原来数据库中的数据都会被删除。所以需要用到MySQLiteHelper中的update方法。

    class MySQLiteHelper extends SQLiteOpenHelper{
    	.....
    	@Override
    	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
    		db.execSQL("drop table if exists book");  //如果已经存在就删除,防止重复创建
    		onCreate(db);  // 再次执行onCreate 方法
    	}
    }
    

    但是onUpgrade方法默认是不执行的,如何让onUpgrade方法执行,需要用到MySQLiteHelper 构造参数中的版本号:

    private static final int DATABASE_VERSION = 1;//  将版本号 由 1 改为2 
    

    这里将数据库版本号由1改为2,表示对数据库的升级

    数据库的增删改查

    添加数据

    ContentValues values = new ContentValues();
    	values.put("date", str1.isEmpty()?"0.0":str1);
    	values.put("breakfast", ""+str2);
    	values.put("lunch", ""+str3);
    	values.put("dinner", ""+str4);
    	values.put("happy", ""+str5);
    	values.put("other", ""+str6);
    	values.put("spare", ""+str7);
    	long ii = db.insert("kog_bill", "", values);
    	values.clear();
    	if(ii != -1) {
    		Utils.showToast("保存成功!", MainActivity.this);
    	}else {
    		Utils.showToast("保存失败!", MainActivity.this);
    	}
    

    更新数据

    ContentValues valus = new ContentValues();
    valuse.put("other","12");
    db.update("kogBill", values, "_id=?",new String[]{id});
    

    删除数据

    db.delete("kogBill", "_id=?",new String[]{id});
    

    查询数据

    db.query("kog_bill", new String[]{"_id","date","breakfast","lunch","dinner","happy","other","spare"}
    		, null, null, null, null, "date desc");
    
    使用SQL操作数据库

    虽然Android 已经给我们提供了非常方便的API用于操作数据库,不过总会有些人不习惯去使用这些辅助行的方法,而是更加青睐于直接使用SQL来操作数据库,当然Android也是提供的。
    添加数据

    db.execSQL("insert into kogBill ("date","breakfest","lunch","dinner","happy","other","spare") values (?,?,?,?,?,?,?)", new String[]{"1921-1-1",“123”,“1”,“1”,“11”,“2”,“3”});
    

    更新数据

    db.execSQL("update kogBill set other = ? where _id = ? ", new String[]{"12",id});
    

    删除数据

    db.execSQL("delete from kogBill where _id = ?”, new String[]{id});
    
    使用 LitePal 操作数据库

    假设编译环境为AndroidStudio。
    1,引进包

    dependencies{
    	compile fileTree(dir:'libs', include:['*.jar'])
    	compile 'com.android.support:appcompat-v7:23.2.0'
    	testCompile 'junt:junt:4.12'
    	compile 'org.litepal.android:core:1.3.2'   //引入litepal包
    }
    

    2,配置litepal.xml 文件
    右键app/src/main 目录->New -> Directory ,创建一个assets目录,然后在 assets目录下再新建一个litepal.xml 文件,接着编辑文件中的内容

    <?xml version='1.0' encoding="utf-8"?>
    <litepal>
    	<dbname value = "BookStore"></dbname>
    	<version value="1"></version>
    	<list></list>
    </listepal>
    

    其中,<dbname 标签用来指定数据库名,<version 用来指定数据库版本号,<list 标签用来指定所有映射模型。
    最后还需要在配置以下 LitePalApplication, 修改AndroidManifest.xml 中的代码

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    	package="com.example.litepaltest" >
    	<application
    		android:name="org.litepal.LitePalApplication"  //配置 LitePalApplication
    		android:allowBackup="true"
    		.....
    	</application>
    </manifest>
    

    以上,LitePal的配置工作已经结束了,接下来使用LitePal。
    首先将需要实现 javabean类 对应 数据库表.
    然后将javabean类添加到映射模型列表中,修改litepal.xml 中的代码

    <litepal>
    	<dbname value="kogBill" ></dbname>
    	<version value="1"></version>
    	<list>
    		<mapping class="com.example.litepaltest.book"></mapping>  //javabean类的路径
    	</list>
    

    这样所有工作就已经完成,现在只要进行任意一次数据库的操作,数据库db文件就会自动创建,比如:

    Connector.getDatabase();
    
    操作数据

    如果需要对某个表进行数据操作,需要让其对应的javaBean类继承DataSupport

    public class Book extends DataSupport { //让对应的类继承DataSupport
    	...
    }
    

    接下来,进行添加数据的操作:

    Book book = new Book();
    book.setName("...");
    book.setAuthor("...");
    book.setPages(234);
    book.setPrice(12,21);
    book.setPress("unkow");
    book.save();  //执行sava 就可以插入数据了
    

    执行更新数据:

    Book book = new Book();
    book.setPrice(11.11);
    book.setPress("Anchor");
    book.updateAll("name = ?  and authro = ?","..","...");
    

    删除数据:

    DataSupport.deleteAll(Book.class, "price<?","13");
    

    查询数据:

    //查询所有
    List<Book> books = DataSupport.findAll(Book.class);  
    // 查询第一条
    List<Book> books = DataSupport.findFirst(Book.class);
    //查询最后一条
    List<Book> books = DataSupport.findLast(Book.class);
    //查询那几列的数据
    List<Book> books = DataSupport.select("name","author).find(Book.class);
    //条件查询, 页面大于400
    List<Book> books = DataSupport.where("pages >?","400").find(Book.class);
    //将 price 降序排序
    List<Book> books = DataSupport.order(price desc").find(Book.class);
    //查询前3条
    List<Book> books = DataSupport.limit(3).find(Book.class);
    //从下表1开始,往后查询3条
    List<Book> boods = 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);
    

    如果有些特殊查询,使用上述方法无法查询时,可以使用如下语句:

    Cursor c  = DataSupport.findBySQL("select * from Book where pages > ? and price < ?”, 
    		"400","20”);
    

    内容提供器(Conent Provider)方式

    网络存储方式

    Android 六大布局

    LinearLayout 线性布局

    线性布局,如名字所描述的那样,这个布局将它所包含的控件在线性方向上一次排列,方向分为 水平方向和数值方向。
    属性 android:orientation = “vertical” | “horizontal” 竖直或水平,默认水平
    属性 android:layout_gravity = “top” | “center” | “bottom” 内部的布局方式
    属性 android:gravity = “top”|"center”|“bottom” 相对于父容器的对齐方式
    属性 android:layout_weidht 使用比例方式执行控件的大小,在手机屏幕适配方面起到非常重要的作用

    TableLayout 表格布局

    表格布局与HTML中的table td tr标签类似

    <table>
    	<tr><td></td></tr>
    </table>
    
    

    如何确定行与列

    • 如果在TableLayout下添加组件,这个组件会占满整行
    • 如果想把多个组件放在同一行,需要添加TableRow的容器,然后把组件放进去
    • TableRow中的组件个数决定的该行的列数,而列的宽度由列中最宽的单元格决定
    • TableRow嗯layout_width属性默认是fill-parent,修改无效。但是layout_height默认是wrapcontent,可以修改
    • 整个表格的宽度取决于父容器的宽度(占满父容器)

    重要的属性:

    • android:collapaseColumns:设置需要被隐藏的列的序号
    • android:shrinkColumns:设置允许被收缩的列的序号
    • android:stretchCoumns:设置运行被拉伸嗯列的序号
      这三个属性都是从0开始算的
    shrinkColumns= "2" //对应第三行
    shrinkColumns = '"0,2" //设置多个都生效
    shrinkColumns = "" //所有列都生效
    
    • android:layout_column=“2”: 表示跳过第二个,直接显示第三个,从1开始
    • android:layout_span=“4”:表示合并*4个单元格,也就说这个组件占4个单元格

    FrameLayout 帧布局

    FrameLayout布局在不使用layout_gravity属性的情况下,布局中的多项元素会在父容器的左上角重叠,使用layout_gravity 属性,可以设置不同的位置。

    在这里插入图片描述

    重要属性

    • top、bottom、left、right:将对象放在其容器的上、下、左、右的位置
    • center、center_vertical、center_horizontal:讲对象居中、水平居中、竖直居中
      在这里插入图片描述
      注意 :区分 “android:gravity”“android:layout_gravity”
      • android:gravity :是对控件本身而言,控制控件自身的内容在控件中的位置
      • android:layout_gravity:是相对于控件父容器而言,设置该控件在其父容器中的位置

    RelativeLayout 相对布局

    相对布局是用的比较多的一种布局。因为布局套用越多,布局加载越慢,如果使用相对布局,紧紧只需要一次布局。一般使用布局都是相对布局+线性布局使用。
    相对布局主要记住重要的属性

    此图来自:https://www.runoob.com/w3cnote/android-tutorial-relativelayout.html
    此图来自:https://www.runoob.com/w3cnote/android-tutorial-relativelayout.html

    注意marginpadding 的区别
    * margin:代表的是偏移,是相对于父容器而言
    * padding:代表的是填充,是本组件内部而言

    GridLayout 网格布局

    网格布局与TableLayout(表格布局)类似,不过网格布局功能更多,也更好用。

    • 可以设置布局中组件的排列方式
    • 可以设置网格布局有几行几列
    • 可以直接设置组件的位置,位于某行某列
    • 可以直接设置组件占多少行多少列

    使用网格布局,需要先设置排列方式、对齐方式、行数、列数。然后对布局中的子组件进行行列设置。
    在这里插入图片描述
    图片来自:https://blog.csdn.net/wangmx1993328/article/details/82770910

    下例是计算器布局:

    在这里插入图片描述

    <GridLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:columnCount="4"
            android:orientation="horizontal"
            android:rowCount="6">
            <TextView
                android:layout_columnSpan="4"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="5dp"
                android:background="#ffcccccc"
                android:text="0"
                android:textSize="50sp"
                android:layout_gravity="fill" />
            <Button
                android:layout_columnSpan="1"
                android:text="1"
                android:layout_gravity="fill" />
            <Button
                android:text="2"
                android:layout_gravity="fill" />
            <Button
                android:text="3"
                android:layout_gravity="fill" />
            <Button
                android:text="+"
                android:layout_gravity="fill" />
    
            <Button
                android:layout_columnSpan="1"
                android:text="4"
                android:layout_gravity="fill" />
            <Button
                android:text="5"
                android:layout_gravity="fill" />
            <Button
                android:text="6"
                android:layout_gravity="fill" />
            <Button
                android:text="-"
                android:layout_gravity="fill" />
            <Button
                android:layout_columnSpan="1"
                android:text="7"
                android:layout_gravity="fill" />
            <Button
                android:text="8"
                android:layout_gravity="fill" />
            <Button
                android:text="9"
                android:layout_gravity="fill" />
            <Button
                android:text="*"
                android:layout_gravity="fill" />
            <Button
                android:layout_columnSpan="1"
                android:text=""
                android:layout_gravity="fill" />
            <Button
                android:text="0"
                android:layout_gravity="fill" />
            <Button
                android:text="="
                android:layout_gravity="fill" />
            <Button
                android:text="/"
                android:layout_gravity="fill" />
        </GridLayout>
    

    AbsoluteLayout 绝对布局

    绝对布局放在最后,是因为绝对布局基本上是不使用的。一般布局需要适配不同机型,如果使用绝对布局,在不同尺寸的手机上显示会变形。

    重要属性

    • android:layout_x:设置组件的x坐标
    • android:layout_y:设置组件的y坐标

    在这里插入图片描述

    <AbsoluteLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:id="@+id/button1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_x="50dp"
                android:layout_y="100dp"
                android:text="=" />
    
            <Button
                android:id="@+id/button2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_x="150dp"
                android:layout_y="100dp"
                android:text="=" />
    
            <Button
                android:id="@+id/button3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_x="100dp"
                android:layout_y="120dp"
                android:text="." />
    
        </AbsoluteLayout>
    
    展开全文
  • Android四大组件详解

    万次阅读 多人点赞 2019-06-14 09:31:39
    Android四大组件详解 Android四大组件分别为activity、service、content provider、broadcast receiver。 新的改变 1、activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent...

    第一次写博客,也是第一次接触安卓,很多格式什么的都不熟悉,如果比较乱还请多多指教。

    关注微信公众号,一起探索

    在这里插入图片描述

    Android四大组件详解

    Android四大组件分别为activity、service、content provider、broadcast receiver。

    1、activity

    (1)一个Activity通常就是一个单独的屏幕(窗口)。

    (2)Activity之间通过Intent进行通信。

    (3)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity。

    2、service

    (1)service用于在后台完成用户指定的操作。service分为两种:

    (a)started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。

    (b)bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。

    (2)startService()与bindService()区别:

    (a)started service(启动服务)是由其他组件调用startService()方法启动的,这导致服务的onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止。

    (b)使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。

    (3)开发人员需要在应用程序配置文件中声明全部的service,使用标签。

    (4)Service通常位于后台运行,它一般不需要与用户交互,因此Service组件没有图形用户界面。Service组件需要继承Service基类。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。

    3、content provider

    (1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。

    (2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。

    (3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。

    (4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。

    (5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。

    4、broadcast receiver

    (1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

    (2)广播接收者的注册有两种方法,分别是程序动态注册和AndroidManifest文件中进行静态注册。

    (3)动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。

    (4)Android还有一套本地广播机制,就是为了解决广播的安全问题,因为系统全局广播可以被其他任何程序接收到,一些携带关键性数据的广播就可能被其他应用程序截获。而本地广播机制发出的广播只能在应用程序的内部进行传递,并且只能接收来自本应用程序的广播,这样就不存在安全问题了。

    (5)今天了解了Android的静态注册和动态注册,Android在8.0以后,为了提高效率,删除了静态注册,防止关闭App后广播还在,造成内存泄漏。现在静态注册的广播需要指定包名,而动态注册就没有这个问题。并且,无论是静态注册广播还是动态注册广播,在接收广播的时候都不能拦截广播,否则会报错。
    谷歌官网的原文是:应用无法使用其清单注册大部分隐式广播。不过,是不能对大部分的广播进行注册,但还是有些广播可以进行静态注册的,比如对接收Android开机的广播通过静态注册还是能够正常接收的。

    5、Intent介绍

    Intent是Android系统用来抽象描述要执行的一个操作,也可以在不同组件之间进行沟通和消息传递。
    显式的Intent就是你已经知道要启动的组件名称,比如某个Activity的包名和类名,在Intent中明确的指定了这个组件(Activity),一般来说这种Intent经常用在一个应用中,因为你已经明确的知道要启动的组件名称。
    隐式的Intent就是你不知道要启动的组件名称,只知道一个Intent动作要执行,比如:拍照,录像,查看地图。一般来说这种Intent用在不同的应用之间传递信息。

    展开全文
  • Android 四大组件系列详解: Android 四大组件之—Activity 详解 Android 四大组件系列之—-Service 详解 Android 四大组件系列之—... Activity作为Android四大组件之一,它有着举足轻重的地位,每...
  • Android 四大组件的理解(组件内容)Android四大组件是一个App的基石,四大组件相信大家都知道哪四个,Activity,BroadCast,ContentProvider,Service。 (组件职责)Activity提供与UI界面相关的管理;Service提供与...
  • Android四大组件

    2015-05-13 10:53:26
    Android四大组建

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,936
精华内容 15,974
关键字:

android四大组件系列