Category Archives: 7. Broadcast Receiver

Bài 3: Custom Broadcast Reveice và truyền dữ liệu giữa hai project

Bài 3: Custom Broadcast Reveice và truyền dữ liệu giữa hai project

Hai bài trước các bạn đã đi hết các cách cơ sở để viết một ứng dụng nhận Broadcast từ hệ thống. Bài hôm nay mình giới thiệu tới các bạn cách gửi một Broadcast và nhận một Broadcast không phải từ hệ thống mà từ ứng dụng này sang ứng dụng khác.

Screenshot from 2014-07-14 07:11:52 

Screenshot from 2014-07-14 07:12:02

Theo hình trên Project 1 mình sẽ gửi một Broadcast, Project 2 sẽ nhận Broadcast đó và hiển thị một Toast lên màn hình.

Viết một ứng dụng nhận một Broadcast các bạn đã biết ở bài trước, nó chỉ khác duy nhất action trong thẻ <action> không phải là các action cố định mà hệ thống đã định nghĩa trước đó mà action này do ta định nghĩ

1
2
3
<intent-filter >
    <action android:name="clbtinhoc.ictu.custom_intent"/>
</intent-filter>

Ta sẽ đi luôn vào viết ứng dụng gửi broadcast trước, bạn tạo một project trong đó file main_activity.xml (file giao diện) có nội dung là một Button

<?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"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Broadcast Receiver custom"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_gravity="center_horizontal"/>
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send Broadcast"
        android:layout_gravity="center_horizontal"/>
</LinearLayout>
Tại file ActivityMain.java các bạn viết một đoạn code gửi broadcast khi ấn Button
import clbtinhoc.ictu.broadcastreceivercustom.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class ActivityMain extends Activity{
    
    @Override
    public void onCreate(Bundle bundle){
        super.onCreate(bundle);
        setContentView(R.layout.main_activity);
        
        final Button button = (Button) findViewById(R.id.button1);
        button.setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // intent kèm theo hành động có tên clbtinhoc.ictu.custom_intent
                Intent intent = new Intent("clbtinhoc.ictu.custom_intent");
                Bundle bundle = new Bundle();
                bundle.putString("xau", "Xin chào!");
                intent.putExtras(bundle);
                // gửi Broadcast
                sendBroadcast(intent);
            }
        });
        
    }
}

Đoạn code khá đơn giản nên mình chỉ chú thích duy nhất hàm gửi broadcast thôi 🙂

Tiếp theo bạn tạo một Project mới dùng để nhận broadcast. Project này khi tạo không có file Activity.java. Bạn tạo một class có tên MyProject.java sau đó extends từ BroadcastReceiver

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
public class MyBroadcast extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle bundle = intent.getExtras();
        String text = bundle.getString("xau");
        
        Toast.makeText(context, text, Toast.LENGTH_SHORT).show();  
    }
}
Tại file AndroidMaintifest.xml các bạn đăng ký nhận broadcast với nội dung như sau:
    package="clbtinhoc.ictu.broadcastreceivercustom"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        
        <receiver android:name="clbtinhoc.ictu.MyBroadcast">
            <intent-filter >
                <action android:name="clbtinhoc.ictu.custom_intent"/>
            </intent-filter>
        </receiver>
    </application>
</manifest>

Code không có gì mới chỉ có phần thẻ action với nội dung là “clbtinhoc.ictu.custom_intent”

Các bạn build project số hai vào máy trước sau đó build và chạy project số 1 sau đó ấn button sẽ thấy hiện một Toast.

Ở project mẫu mình sẽ hợp luôn hai project làm một project các bạn có thể download ở đây:****

Bài 2- Broadcast Receiver trong Android, đăng ký trong AndroidMaintifest và chương trình nhận tin nhắn

Bài 2- Broadcast Receiver trong Android, đăng ký trong AndroidMaintifest và chương trình nhận tin nhắn

Ở bài trước các bạn đã biết cách đăng ký BroadcastReceiver trong Android thông qua coding để nhận biết các sự kiện của hệ thống. Bài này mình giới thiệu tiếp tới các bạn cách thứ hai để đăng ký, sử dụng xml đăng ký trong AndroidMaintifest, cách này được sử dụng phổ biến và rộng dãi hơn bởi ngay cả khi ứng dụng đang tắt nó vẫn có thể nhận được thông tin từ hệ thống và khởi động ứng dụng.

Cách đăng ký trong Android Maintifest có vẻ đơn giản hơn chỉ với thẻ receiver và intentfilter

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <!-- Đăng ký nhận BroadcastReceiver cho class MyBroadcastReceiver-->
    <receiver
        android:name="clbtinhoc.ictu.onreceiversms.MyBroadcastReceiver"
        android:label="On Receiver SMS" >
        <!-- Đăng ký lọc hành động nhận tin nhắn -->
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver >
</application>

Để xử lý thông tin khi nhận được sự kiện nhận tin nhắn thì trong mã java ta không cần phải tạo một Activity mà chỉ cần tạo một class extends từ BroadcastReceiver, cụ thể mình sẽ demo một chương trình nhận tin nhắn như sau:
Screenshot from 2014-07-12 16:34:47

Các bạn thấy ở hình trên có một tin nhắn đến và được hiển thị ra một Dialog, số gửi tin là 5554 đây là port của máy ảo thôi, cách gửi tin nhắn đến máy ảo mình sẽ trình bày ở cuối bài viết.

Đầu tiên các bạn tạo một Project Android rỗng không có class nào cả nhé!

 

Screenshot from 2014-07-12 12:11:26

Ta sẽ đi lần lượt từng bước một, đầu tiên các bạn nhấn chuột phải vào thư mục src sau đó chọn tạo một class đặt tên là MyBroadcastReceiver.java có nội dung như sau:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.widget.Toast;
public class MyBroadcastReceiver extends BroadcastReceiver {
    // Phương thức này sẽ được gọi khi chương trình nhận được sự kiện có tin nhắn đến
    @Override
    public void onReceive(Context context, Intent intent) {
        processSMS(context, intent);
    }
    public void processSMS(Context context, Intent intent) {
        // lấy đối tượng bundle mình đã nói ở loạt bài về intent
        Bundle bundle = intent.getExtras();
        // Do hệ thống trả về một loạt các tin nhắn đến cùng lúc nên phải dùng mảng
        Object[] array = (Object[]) bundle.get("pdus"); // hành động "pdus" để lấy gói tin nhắn
    
        for (int i = 0; i < array.length; i++) {
            // lệnh này dùng được chuyển đổi Object về tin nhắn createFromPdu
            SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) array[i]);
            // lấy nội dung tin nhắn
            String msg = smsMessage.getMessageBody();
            // lấy số điện thoại tin nhắn
            String number = smsMessage.getDisplayOriginatingAddress();
            // Dùng Toast để hiển thị tin nhắn
//          Toast.makeText(context, number + "\n" + msg, Toast.LENGTH_LONG).show();
            
            // Hiển thị Tin nhắn lên một Dialog
            showDialog(context, intent, number, msg);
        }
        // Hiển thị tin nhắn cuối cùng trong số loạt tin nhắn nhận được lên Activity
    }
    
    public void showDialog(Context context, Intent intent, String number, String msg){
        /*
         * vì MyBroadcastReceiver không kế thừa context (Activity) nên khi tạo intent mới không truyền
         * this vào được, mà phải truyền cái context đã được gửi kèm
         */
        // Gửi dữ liệu lên Activity mới
        Intent i = new Intent(context, MyView.class);
        Bundle bundle2 = new Bundle();
        bundle2.putString("PHONE", number);
        bundle2.putString("SMS", msg);
        i.putExtra("GOITIN", bundle2);
        /* Do đang làm việc trong BroadcastReceiver và một số vấn đề liên quan tới task
         * trong Android nên phải thêm cờ FLAG_ACTIVITY_NEW_TASK
         */
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(i); // cũng vì lý do trên nên phải dùng context để khởi động Activity
    }
}

Tại mã java xử lý trên chúng ta thấy nó không được kế thừa từ Activity như thông thường mà lại kế thừa từ BroadcastReceiver do đó để khởi động một Activity khi nhận được tin nhắn ta phải thông qua context mà được gửi kèm theo.
Đoạn mã trên có lệnh mở một Activity mang tên MyView.class nên ta sẽ tạo một class MyView nằm trong cùng package với MyBroadcastReceiver có nội dung như sau:

Class này chỉ làm nhiệm vụ đọc lại dữ liệu gửi từ MyBroadcastReceiver sau đó hiển thị lên giao diện.

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MyView extends Activity{
    @Override
    public void onCreate(Bundle bundle){
        super.onCreate(bundle);
        setContentView(R.layout.layout_dialog);
        
        final TextView textView = (TextView) findViewById(R.id.textView1);
        
        Intent intent = getIntent();
        Bundle b = intent.getBundleExtra("GOITIN");
        String sms = b.getString("SMS");
        String number = b.getString("PHONE");
        
        this.setTitle(number);
        textView.setText(sms);
        
        ((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
    
}

Đoạn code trên đã quá quen thuộc nên mình cũng không giới thiệu nhiều nữa.

Việc quan trọng nhất khi xây dựng ứng dụng nhận tin nhắn bạn phải đăng ký hành động nhận tin trong AndroidMaintifest:

 

    package="clbtinhoc.ictu.onreceiversms"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <!-- Đăng ký nhận thông báo từ BroadcastReceiver cho class MyBroadcastReceiver-->
        <receiver
            android:name="clbtinhoc.ictu.onreceiversms.MyBroadcastReceiver"
            android:label="On Receiver SMS" >
            <!-- Đăng ký lọc hành động nhận tin nhắn -->
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver >
        <activity
            android:name="clbtinhoc.ictu.onreceiversms.MyView"
            android:label="SMS"
            android:theme="@android:style/Theme.Dialog">
        </activity>
    </application>
</manifest>

Bình thường một ứng dụng sẽ không được phép truy cập ngẫu nhiên vào các thành phần mà hệ thống “cấm” như: Nhận tin nhắn, gửi tin, gọi điện,… do đó, ngoài việc đăng ký broadcastReceiver nhận tin nhắn bằng thẻ receiver ta còn phải đăng ký với hệ thống cho phép ứng dụng được nhận tin nhắn.

Ở trên ta có tạo một Activity MyView, nó sẽ được khởi động khi ứng dụng nhận được tin nhắn do đó trong AndroidMaintifest cũng phải đăng ký bằng thẻ . Thể hiện tính chuyên nghiệp của ứng dụn nhắn tin ta sẽ cho tin nhắn hiển thị thông qua Dialog bằng cách đặt Theme cho Activity MyView: “@android:style/Theme.Dialog”.

Ngay bây giờ bạn hãy chạy ứng dụng, khi chạy bạn sẽ không thấy có hiện tượng gì, tìm trong menu app cũng không thấy app vừa cài đâu cả là vì ứng dụng của ta không đăng ký là một chương trình laucher. Bạn thử gửi một tin nhắn vào máy mình xem, nếu mà không hiện tin nhắn bạn hãy vào phần quản lý ứng dụng xóa mặc định của các chương trình đọc tin nhắn khác đi nhé!

Note:
Với những bạn sử dụng máy ảo để test thì cách gửi tin nhắn vào máy ảo cũng khá đơn giản:
Bước 1: tại thẻ window của eclipse chọn Open Perspective\DDMS sẽ ra giao diện như hình dưới.

Screenshot from 2014-07-12 17:22:44

Bước 2: Tại thẻ Emulator trong mục Telephony Actions phần Incoming number bạn nhập số điện thoại của máy ảo. Máy ảo coi số hiệu port là số điện thoại luôn, để xem số hiệu port của máy ảo bạn mở máy ảo lên nhìn vào thanh tiêu đề ấy.

Screenshot from 2014-07-12 17:25:02

Như hình trên thì số hiệu cổng của máy ảo là 5554.

Bước 3: Chọn mục SMS sau đó nhập tin nhắn vào ô Message rồi ấn nút send là được.

Các bạn có thể download code mẫu ở đây:***

Bài 1: Broadcast Receiver trong Android, đăng ký bằng coding

Bài 1: Broadcast Receiver trong Android, đăng ký bằng coding

Nối tiếp loạt bài về Intent bài này mình giới thiệu tới các bạn một thành phần cũng rất quan trọng trong Android là BroadcastReceiver nó là một trong những thành phần chính của Android.  BroadcastReceiver dùng để nhận các intent từ hệ thống hoặc trao đổi dữ liệu giữa hai hay nhiều ứng dụng. Nói là nhận các intent từ hệ thống mới nghe có vẻ khó hiểu do đó mình sẽ có một ví dụ cụ thể sau:

Chắc hẳn nhiều bạn mới học Android mà có tính tò mò thì sẽ tự hỏi “khi có một tin nhắn đến phần mềm làm sao có thể biết được tin nhắn đến” hay là làm sao để biết có cuộc gọi đến? đang sạc pin hay đã rút sạc,…. Android cung cấp riêng một thành phần BroadcastReceiver dùng để nhận biết các thông tin đó từ hệ điều hành.

Một ứng dụng muốn nhận được các thông tin từ hệ thống nó phải đăng ký sử dụng BroadcastReceiver, đăng ký có hai cách:

1. Đăng ký ngay trong coding: Cách này ít được dùng vì chỉ khi ứng dụng mở lên nó mới nhận các thông tin từ hệ thống, khi ứng dụng tắt đi thì nó cũng không nhận được nữa.

2. Đăng ký trong AndroidMaintifest.xml: Cách này được sử dụng rất nhiều, với cách đăng ký này ngay cả khi ứng dụng của bạn đang tắt nó vẫn có thể nhận được thông tin từ hệ thống, khi nhận được thông tin nó lập tức khởi động ứng dụng (Bạn thử liên tưởng tới phần mềm nhắn tin xem, khi có tin cái là nó tự mở luôn 🙂 ).

Bài hôm nay mình sẽ trình bày tới các bạn cách thứ nhất đăng ký trong coding với demo nhận biết trạng thái sạc pin/ không sạc pin.

Screenshot_2014-07-12-13-49-00 Screenshot_2014-07-12-13-49-07

Đăng ký nhận thông tin từ hệ thống chúng ta phải tạo một đối tượng IntentFilter để lọc những thông tin cần thiết, ở đây chúng ta sẽ lọc hai sự kiện cắm sạc và rút sạc.

IntentFilter filter = new IntentFilter();
// Đăng ký lọc sự kiện cắm sạc và rút sạc cho intentfilter
filter.addAction("android.intent.action.ACTION_POWER_CONNECTED");
filter.addAction("android.intent.action.ACTION_POWER_DISCONNECTED");
Sau khi đăng ký lọc sự kiện ta phải đăng ký với hệ thống xử lý các sự kiện đó qua BroadcastReceiver
// Khởi tạo BroadcastReceiver
BroadcastReceiver receiver = new BroadcastReceiver() {
    // Phương thức này sẽ được hệ thống gọi khi nhận được sự kiện đang sạc pin
    @Override
    public void onReceive(Context context, Intent intent) {
        // nội dung công việc cần làm khi sự kiện đã đăng ký xảy ra
    }
}
// Đăng ký nhận và xử lý các sự kiện
registerReceiver(receiver, filter); // được viết trong Activity
Ta sẽ đi vào viết mã cho app với file giao diện trước, về giao diện thì chỉ có một TextView
<?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"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
Đăng ký cho hệ thống biết chương trình sẽ nhận thông tin bộ sạc ActivityMain.java
import opin.onreceiversms_incodeding.R;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.widget.TextView;
public class ActivityMain extends Activity {
    private BroadcastReceiver receiver = null;
    @Override
    public void onCreate(Bundle bundle){
        super.onCreate(bundle);
        setContentView(R.layout.main_activity);
        final TextView textView = (TextView) findViewById(R.id.textView1);
        IntentFilter filter = new IntentFilter();
        // Đăng ký lọc sự kiện cắm sạc và rút sạc cho intentfilter
        filter.addAction("android.intent.action.ACTION_POWER_CONNECTED");
        filter.addAction("android.intent.action.ACTION_POWER_DISCONNECTED");
        receiver = new BroadcastReceiver() {
            // Phương thức này sẽ được hệ thống gọi khi nhận được sự kiện đang sạc pin
            @Override
            public void onReceive(Context context, Intent intent) {
                // nếu sự kiện nhận được là kết nối sạc
                if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)){
                    textView.setText(" Đang sạc pin");
                }
                // nếu sự kiện nhận đưọc là rút sạc
                if(intent.getAction().endsWith(Intent.ACTION_POWER_DISCONNECTED)){
                    textView.setText(" Đã rút sạc");
                }
            }
        };
        // Đăng ký nhận thông tin với hệ thống
        registerReceiver(receiver, filter);
    }
    @Override
    public void onDestroy(){
        // hủy đăng ký
        if (receiver != null) {
            unregisterReceiver(receiver);
        }
        super.onDestroy();
    }
}

Ở trên các bạn thấy lệnh:
filter.addAction(“android.intent.action.ACTION_POWER_CONNECTED”);
Để biết được các hành động nào có thể đăng ký mình sẽ hướng dẫn ở bài sau. hoặc bạn có thể xem một số hành động ở đây:

http://developer.android.com/reference/android/content/Intent.html

Các khâu thủ tục như vậy là xong, các bạn có thể chạy sau đó thử cắm và rút sạc xem sao.

Các bạn có thể download code mẫu ở đây:****

Nội dung bài học

————————-Broadcast Receiver trong Android————————–

Bài 1: Broadcast Receiver trong Android, đăng ký bằng coding

Bài 2: Broadcast Receiver trong Android, đăng ký trong AndroidMaintifest

Bài 3: Custom Broadcast Receiver và truyền dữ liệu giữa hai ứng dụng