? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ------- android培训、java培训、期待与您交流! ----------
?
昨天夜里睡觉,用手机上黑马的论坛的时候,读到一篇感谢信:8个月前helloworld出错十几个到5天看懂一个w行项目 文章。感触良多,果然黑马是屌丝逆袭之地啊。所以我也不要不甘落啊
?
里面提到了一个aidl的知识。我不太了解,就是是面试的时候被问到的问题,我还是提前一些准备吧
这次先学习aidl的基础知识
摘抄一点关键:
Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。
为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、Broadcast和Content Provider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。
?
说实话,基本上我还比较晕,反正现在首先认为合格adil是一种服务,这样说吧,我暂且认为Activity、Broadcast和Content Provider和service有这样一种能力:能够跨进程访问。我记得原来在将Content Provider的时候,有一个例子,就是比如说某一个activity需要通讯录里面的全部的联系人的姓名和电话,但是这个activity和通讯录这个activity是属于不同的进程的,所以这个content provider就可以将这个通讯录activity里面的联系人的姓名和电话提供给需要的activity,从而实现了进程间的通信。我感觉现在我对基本概念还是不太熟悉,这个aidl我查了半天队我来说有点高深,所以我打算从更加基本的入手。
也就是android 进程间的通信,百度一下,很多人写了这方面的文章。
找了一篇:
Android为了屏蔽进程的概念,利用不同的组件[Activity、Service]来表示进程之间的通信!
组件间通信的核心机制是Intent,通过Intent可以开启一个Activity或Service,不论这个Activity或Service是属于当前应用还是其它应用的!
?
如果是讲到intent的话,我就非常熟悉了,在mp3项目中使用的非常多。主要是不同的activity的切换,还有就是比如说点击某首歌曲可以启动音乐播放的service,这样也属于进程间的通信。我原来学操作系统的时候还总是有进程的理论知识,今天读到这句Android为了屏蔽进程的概念,利用不同的组件[Activity、Service]来表示进程之间的通信!终于明白了原来我学的知识无存不在。
?
?
接着:Intent类型:
1、显式--直接指定消息目的地,只适合同一进程内的不同组件之间通信
new Intent(this,Target.class)
2、隐式--AndroidMainifest.xml中注册,一般用于跨进程通信
new Intent(String action)
?
第一种显式用的很多。第二种就不太了解了。一般用于跨进程通信,我读到这句话的时候,认识到不是每一个组件(activity)就是一个进程,也可以一个进程控制多个activity,但是MP3项目中的播放service是一个进程,这个我可以肯定。
?
接着读了一段文章,我还有点晕。我只能把,我懂的挑出来。原来只有一个project有多activity,但是不清楚,为什么一打开project会跳出某个activity
原来是:
一个<activity>包括零个或多个<intent-filter>,它主要是作为匹配的标准,能否匹配成功由<action>、<category>、<data>三个tag共同决定的。
?
一个<intent-filter>包括:一个或多个 <action> 和零个或多个 <category>?
?
其中<action>
?
?
我再回过头查看MP3项目,果然有类似的语句:android.intent.action.MAIN 这句话是当打开这个应用时第一个启动的activity。这句话与下面的category android:name="android.intent.category.LAUNCHER"同时存在
?
?
关于<category>?
eg:
<category android :name="android.intent.category.LAUNCHER" />
--说明该<activity>是该project运行 的第一个界面?
?
<category android:name="android.intent.category.HOME" />
--说明该<activity>可以作为Launcher的,即系统 操作界面
即实现按下home键,启动自己的launcher.
?
<category android:name="android.intent.category.DEFAULT" />
--缺省情况
?
零个或一个 <data> 这个内容我没用过,所以不太懂。就不展开了。
?
3、一个Intent对应多种匹配结果的处理说明
?一个intent有多个可匹配的处理组件,系统如何处理?
分响应消息的组件类型:
1)如果是service那么这些service都可以启动并处理消息。
2)如果是Activity则会弹出一个对话框让用户进行选择。
?
?
4、安全性问题,最终读懂的只有permission的使用,这个我也知道。反正要用到项目中要用到特殊的功能的时候都需要特别的声明permission。
?
接着讲了IPC机制,读完它的IPC的理论知识的介绍,我终于明白IPC(Inter-Process Communication,进程间通信)的存在含义了
?
如果用播放的activity打开播放的service,用intent就很简单的实现了,这是一种消息机制,紧紧是打开一个service。但是我们如果要在播放的activity(比如1号进程),去控制播放的service(2号进程)的音乐暂停之类的功能,就是利用android的IPC。当然我在项目中已经用了,但是我是不知道罢了,好了,我觉得我了解到这儿我就可以了。因为下面的举例代码略显复杂,按我学习的规律是先学一点现象,再深入,再自己操作。不要一下搞的太猛了。现在我要狠狠的记住这个ipc到底在项目中那里存在就可以了。
?
?
?
?
我再找找别的文章。反正现在是先把难的放一放,深刻理解一下我有粗略印象的。由于那篇黑马同学的文章里,紧接着提到的是BroadcastReceiever的机制,我随便也总结在这里,
因为我之前学过相关内容并且有源码,并且对其理解还是比较深刻的。我只是想执行一下,试试效果,但是我遇到一个非常诡异的问题。就是当我执行mars老师教学视频第21课广播教学的源码无效果,我知道原因但是无法解决,为什么我按住ctrl左键点击<receiver android:name=".TestReceiver">的.TestReceiver会在左下角显示红字Could not open link,但是我同样点击
<activity android:name=".TestActivity",就肯定能跳转到相关页面,我相信就是因为无法跳转,所以才导致,我发送的广播也不能顺利接收。我已在黑马论坛上提问。
?
http://bbs.itheima.com/forum.php?mod=viewthread&tid=38572&page=1#pid231984
?
最后,我终于明白,原来本来就该Could not open link。但是这位仁兄发现intent的action设置错误之后,我改了之后果然好了,居然是这样的错误让我纠结了很久。
?
另外,其实我还有另一个demon,另一个demon更好一些,就贴在这里以后好复习。
?
?
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcastreceiever" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!--注册 recevier,也就是说说刚刚那个类HelloBroadcastRecevier,我们都要在这里写好激活它的条件 --> <receiver android:name=".HelloBroadcastRecevier"> <intent-filter > <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> <intent-filter> <action android:name="android.basic.lesson21.Hello"> </action></intent-filter> </receiver> </application></manifest>
?
package com.example.broadcastreceiever;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.media.MediaPlayer;import android.util.Log; public class HelloBroadcastRecevier extends BroadcastReceiver{ //当接收到intent就会执行下面这一段代码 @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub //输入日志,只是用来确认一下有没有收到.注意当我点击了按钮,也会有打印下面两句的。 System.out.println("BOOT_COMPLETED!!!!!!!!!!!!!!!1"); System.out.println("intent.getAction():"+intent.getAction()); if(intent.getAction().equals("android.basic.lesson21.Hello")){ System.out.println("点击了按钮!!!!!!!!!!!!!!!1"); System.out.println("intent.getAction():"+intent.getStringExtra("yaoyao"));//注意是用getStringExtra来得到putExtra的东西 } //播放一首音乐 MediaPlayer.create(context, R.raw.zuiqingfeng).start();//要在res/raw文件下面放对应的歌。 }}
?
package com.example.broadcastreceiever;import android.os.Bundle;import android.app.Activity;import android.content.Intent;import android.view.Menu;import android.view.View;import android.widget.Button;public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button b1 = (Button) findViewById(R.id.Button01); b1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //定义一个intent Intent intent = new Intent().setAction( "android.basic.lesson21.Hello").putExtra("yaoyao", "yaoyao is 189 days old ,27 weeks -- 2010-08-10"); //广播出去 sendBroadcast(intent); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; }}
?
?
?
?
?
我再说一下前面的错误:我也不知道为什么上面这个贴的demon,曾经失效了,就当我启动模拟器的时候,本来该放一首歌的,但是就是放不出来,然后我去点按钮,按钮点了也无效。这样导致我就去按住ctrl点<receiver android:name=".HelloBroadcastRecevier">的HelloBroadcastRecevier,谁知道,它本来就该是could not open link,和activity不一样。
?
那为什么没放出歌,后来我发现是放了的。
但是看控制台输出,
[2013-02-22 10:34:33 - BroadcastReceiever] ------------------------------
[2013-02-22 10:34:33 - BroadcastReceiever] Android Launch!
[2013-02-22 10:34:33 - BroadcastReceiever] adb is running normally.
[2013-02-22 10:34:33 - BroadcastReceiever] Performing com.example.broadcastreceiever.MainActivity activity launch
[2013-02-22 10:34:33 - BroadcastReceiever] Automatic Target Mode: launching new emulator with compatible AVD '2.2'
[2013-02-22 10:34:33 - BroadcastReceiever] Launching a new emulator with Virtual Device '2.2'
[2013-02-22 10:34:36 - BroadcastReceiever] New emulator found: emulator-5554
[2013-02-22 10:34:36 - BroadcastReceiever] Waiting for HOME ('android.process.acore') to be launched...
[2013-02-22 10:35:02 - BroadcastReceiever] HOME is up on device 'emulator-5554'
[2013-02-22 10:35:02 - BroadcastReceiever] Uploading BroadcastReceiever.apk onto device 'emulator-5554'
[2013-02-22 10:35:11 - BroadcastReceiever] Installing BroadcastReceiever.apk...
当执行到这里的时候大概过一会(我想是准备音乐的时间吧)就会播放音乐了。但是大概只放的了5秒,一执行。
[2013-02-22 10:35:35 - BroadcastReceiever] Success!
[2013-02-22 10:35:35 - BroadcastReceiever] Starting activity com.example.broadcastreceiever.MainActivity on device emulator-5554
[2013-02-22 10:35:37 - BroadcastReceiever] ActivityManager: Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.broadcastreceiever/.MainActivity }
?
音乐就会停。如果我没仔细听,或者是通过安卓模拟放的音乐特别小声就没听到。所以我以为广播无效。
再接着,为什么广播会无效呢?结果今天才发现还是因为在AndroidManifest.xml里面注册的 <action android:name="android.basic.lesson21.Hello">与Intent().setAction()的不一致。晕死,作为新人,两个demon都是网上找的,居然2个都犯了同样的错误。让我纠结了好一阵子。
?
?
?
?
?
?
再来看看content provider,今天最大的成就恐怕就是把这一节,虽然mars里面的视频讲了自定义contentprovider,但是又有难度又不常用,我们最常用的只是去用android里面提供好的content provider,看的稍稍熟悉一些,并且对一个例子的的理解也稍稍好些了。
?
?
例子想干的事:我这儿有一个activity,然后想读取通讯录里面的联系人姓名和电话。具体过程如下,这属于不同进程间的通讯吗?还是不同组件间的通讯?
?
注意要添加两个permission:
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
?
访问一个帐户列表在Accounts Service中(Allows access to the list of accounts in the Accounts Service)。我晕,这个暂时没看出个通讯录有什么关系啊。
?
<uses-permission android:name="android.permission.READ_CONTACTS" />
这个倒是一个名字就知道。
?
代码写的很详解,解释我也添加的差不多了。我发现也没什么好解释的了。当我点击按钮就会弹出对话框得到通讯录里面的联系人的姓名和电话号码。
package com.example.contentprovider;import android.app.Activity;import android.content.ContentResolver;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.provider.ContactsContract;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.Toast;public class MainContentProvider extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button b1 = (Button) findViewById(R.id.Button01); OnClickListener ocl = new OnClickListener() { @Override public void onClick(View v) { ContentResolver contentResolver = getContentResolver();//从下面可以看出,是用这个contentresolver去调用query查询语句 // 获得所有的联系人。注意最后是返回一个游标。CONTENT_URI是为了得到整个表的信息,把鼠标放在contacts上面,还可以得到其他的信息 Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); // 循环遍历。这样看来,刚开始这个游标并没有指向第一条记录哦。我们要手动让其指向第一条记录。 if (cursor.moveToFirst()) { //下面给我的感觉是用用列名,拿到列的序号,再用列的序号拿到列的列值。而游标是指的一行。 int idColumn = cursor.getColumnIndex(ContactsContract.Contacts._ID);//用列名拿列的序号 System.out.println("idColumn:"+idColumn); int displayNameColumn = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); System.out.println("displayNameColumn:"+displayNameColumn); do { // 获得联系人的ID号 String contactId = cursor.getString(idColumn);//用列的序号拿到了某一行的对应列的值。 // 获得联系人姓名 String disPlayName = cursor.getString(displayNameColumn); Toast.makeText(MainContentProvider.this, "联系人姓名:"+disPlayName, Toast.LENGTH_LONG).show(); // 查看该联系人有多少个电话号码。如果没有这返回值为0 int phoneCount = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)); if (phoneCount > 0) { // 获得联系人的电话号码列表 Cursor phonesCursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null); if (phonesCursor.moveToFirst()) { do { // 遍历所有的电话号码 String phoneNumber = phonesCursor .getString(phonesCursor .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); Toast.makeText(MainContentProvider.this, "联系人电话:"+phoneNumber, Toast.LENGTH_LONG).show(); } while (phonesCursor.moveToNext()); } } } while (cursor.moveToNext()); } } }; b1.setOnClickListener(ocl); }}
?
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?------- android培训、java培训、期待与您交流! ----------