四大組件之Service_AIDL

[文章內(nèi)容來自Developers]

AIDL(Android 接口定義語言)與您可能使用過的其他 IDL 類似。 您可以利用它定義客戶端與服務(wù)使用進程間通信 (IPC) 進行相互通信時都認可的編程接口莉掂。 在 Android 上袋毙,一個進程通常無法訪問另一個進程的內(nèi)存诬烹。 盡管如此砸烦,進程需要將其對象分解成操作系統(tǒng)能夠識別的原語,并將對象編組成跨越邊界的對象绞吁。 編寫執(zhí)行這一編組操作的代碼是一項繁瑣的工作幢痘,因此 Android 會使用 AIDL 來處理。

:只有允許不同應用的客戶端用 IPC 方式訪問服務(wù)掀泳,并且想要在服務(wù)中處理多線程時雪隧,才有必要使用 AIDL。 如果您不需要執(zhí)行跨越不同應用的并發(fā) IPC员舵,就應該通過實現(xiàn)一個 Binder創(chuàng)建接口对蒲;或者钓觉,如果您想執(zhí)行 IPC护昧,但根本不需要處理多線程倍啥,則使用 Messenger 類來實現(xiàn)接口。無論如何韭邓,在實現(xiàn) AIDL 之前措近,請您務(wù)必理解綁定服務(wù)

在您開始設(shè)計 AIDL 接口之前女淑,要注意 AIDL 接口的調(diào)用是直接函數(shù)調(diào)用瞭郑。 您不應該假設(shè)發(fā)生調(diào)用的線程。 視調(diào)用來自本地進程還是遠程進程中的線程鸭你,實際情況會有所差異屈张。 具體而言:

  • 來自本地進程的調(diào)用在發(fā)起調(diào)用的同一線程內(nèi)執(zhí)行。如果該線程是您的主 UI 線程袱巨,則該線程繼續(xù)在 AIDL 接口中執(zhí)行阁谆。 如果該線程是其他線程,則其便是在服務(wù)中執(zhí)行您的代碼的線程愉老。 因此场绿,只有在本地線程訪問服務(wù)時,您才能完全控制哪些線程在服務(wù)中執(zhí)行(但如果真是這種情況嫉入,您根本不應該使用 AIDL焰盗,而是應該通過實現(xiàn) Binder 類創(chuàng)建接口)。
  • 來自遠程進程的調(diào)用分派自平臺在您的自有進程內(nèi)部維護的線程池咒林。 您必須為來自未知線程的多次并發(fā)傳入調(diào)用做好準備姨谷。 換言之,AIDL 接口的實現(xiàn)必須是完全線程安全實現(xiàn)映九。
  • oneway關(guān)鍵字用于修改遠程調(diào)用的行為。使用該關(guān)鍵字時瞎颗,遠程調(diào)用不會阻塞件甥;它只是發(fā)送事務(wù)數(shù)據(jù)并立即返回捌议。接口的實現(xiàn)最終接收此調(diào)用時,是以正常遠程調(diào)用形式將其作為來自 Binder
    線程池的常規(guī)調(diào)用進行接收引有。 如果 oneway
    用于本地調(diào)用瓣颅,則不會有任何影響,調(diào)用仍是同步調(diào)用譬正。

定義 AIDL 接口


您必須使用 Java 編程語言語法在 .aidl文件中定義 AIDL 接口宫补,然后將它保存在托管服務(wù)的應用以及任何其他綁定到服務(wù)的應用的源代碼(src/目錄)內(nèi)。
您開發(fā)每個包含 .aidl文件的應用時曾我,Android SDK 工具都會生成一個基于該 .aidl文件的 IBinder接口粉怕,并將其保存在項目的 gen/目錄中。服務(wù)必須視情況實現(xiàn) IBinder接口抒巢。然后客戶端應用便可綁定到該服務(wù)贫贝,并調(diào)用 IBinder中的方法來執(zhí)行 IPC。
如需使用 AIDL 創(chuàng)建綁定服務(wù)蛉谜,請執(zhí)行以下步驟:

  • 創(chuàng)建 .aidl 文件
    此文件定義帶有方法簽名的編程接口稚晚。
  • 實現(xiàn)接口
    Android SDK 工具基于您的 .aidl文件,使用 Java 編程語言生成一個接口型诚。此接口具有一個名為 Stub 的內(nèi)部抽象類客燕,用于擴展 Binder類并實現(xiàn) AIDL 接口中的方法。您必須擴展 Stub類并實現(xiàn)方法狰贯。
  • 向客戶端公開該接口
    實現(xiàn) Service并重寫 onBind()以返回 Stub類的實現(xiàn)也搓。

注意:在 AIDL 接口首次發(fā)布后對其進行的任何更改都必須保持向后兼容性,以避免中斷其他應用對您的服務(wù)的使用暮现。 也就是說还绘,因為必須將您的 .aidl文件復制到其他應用,才能讓這些應用訪問您的服務(wù)的接口栖袋,因此您必須保留對原始接口的支持拍顷。

1. 創(chuàng)建 .aidl 文件
AIDL 使用簡單語法,使您能通過可帶參數(shù)和返回值的一個或多個方法來聲明接口塘幅。 參數(shù)和返回值可以是任意類型昔案,甚至可以是其他 AIDL 生成的接口。
您必須使用 Java 編程語言構(gòu)建 .aidl文件电媳。每個 .aidl文件都必須定義單個接口踏揣,并且只需包含接口聲明和方法簽名。
默認情況下匾乓,AIDL 支持下列數(shù)據(jù)類型:

  • Java 編程語言中的所有原語類型(如 int捞稿、long、char、boolean等等)
  • String
  • CharSequence
  • List
    List中的所有元素都必須是以上列表中支持的數(shù)據(jù)類型娱局、其他 AIDL 生成的接口或您聲明的可打包類型彰亥。 可選擇將 List 用作“通用”類(例如,List<String>)衰齐。另一端實際接收的具體類始終是 ArrayList任斋,但生成的方法使用的是 List接口。
  • Map
    Map中的所有元素都必須是以上列表中支持的數(shù)據(jù)類型耻涛、其他 AIDL 生成的接口或您聲明的可打包類型废酷。 不支持通用 Map(如Map<String,Integer>形式的 Map)。 另一端實際接收的具體類始終是 HashMap抹缕,但生成的方法使用的是 Map 接口澈蟆。

您必須為以上未列出的每個附加類型加入一個 import語句,即使這些類型是在與您的接口相同的軟件包中定義歉嗓。
定義服務(wù)接口時丰介,請注意:

  • 方法可帶零個或多個參數(shù),返回值或空值鉴分。
    所有非原語參數(shù)都需要指示數(shù)據(jù)走向的方向標記哮幢。可以是 in志珍、out或 inout(見以下示例)橙垢。原語默認為 in,不能是其他方向伦糯。

注意:您應該將方向限定為真正需要的方向柜某,因為編組參數(shù)的開銷極大。

  • .aidl文件中包括的所有代碼注釋都包含在生成的 IBinder接口中(import 和 package 語句之前的注釋除外)
  • 只支持方法敛纲;您不能公開 AIDL 中的靜態(tài)字段喂击。

以下是一個 .aidl
文件示例:

// IRemoteService.aidlpackage com.example.android;
// Declare any non-default types here with import statements
/**
 Example service interface
 */
interface IRemoteService {
    /**
 Request the process ID of this service, to do evil things with it. 
*/
    int getPid();
    /**
 Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);}

只需將您的 .aidl文件保存在項目的 src/目錄內(nèi),當您開發(fā)應用時淤翔,SDK 工具會在項目的 gen/目錄中生成 IBinder接口文件翰绊。生成的文件名與 .aidl文件名一致,只是使用了 .java擴展名(例如旁壮,IRemoteService.aidl生成的文件名是 IRemoteService.java)监嗜。
如果您使用 Android Studio,增量編譯幾乎會立即生成 Binder 類抡谐。 如果您不使用 Android Studio裁奇,則 Gradle 工具會在您下一次開發(fā)應用時生成 Binder 類 — 您應該在編寫完 .aidl文件后立即用 gradle assembleDebug(或 gradle assembleRelease)編譯項目,以便您的代碼能夠鏈接到生成的類麦撵。

2.實現(xiàn)接口
當您開發(fā)應用時刽肠,Android SDK 工具會生成一個以 .aidl文件命名的 .java接口文件溃肪。生成的接口包括一個名為 Stub的子類,這個子類是其父接口(例如音五,YourInterface.Stub)的抽象實現(xiàn)乍惊,用于聲明 .aidl文件中的所有方法。

注:Stub還定義了幾個幫助程序方法放仗,其中最引人關(guān)注的是 asInterface(),該方法帶 IBinder(通常便是傳遞給客戶端 onServiceConnected()回調(diào)方法的參數(shù))并返回存根接口實例撬碟。 如需了解如何進行這種轉(zhuǎn)換的更多詳細信息诞挨,請參見調(diào)用 IPC 方法。

如需實現(xiàn) .aidl生成的接口呢蛤,請擴展生成的 Binder接口(例如惶傻,YourInterface.Stub)并實現(xiàn)從 .aidl文件繼承的方法。
以下是一個使用匿名實例實現(xiàn)名為 IRemoteService的接口(由以上 IRemoteService.aidl示例定義)的示例:

private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
    public int getPid(){
        return Process.myPid();
    }
    public void basicTypes(int anInt, long aLong, boolean aBoolean,
        float aFloat, double aDouble, String aString) {
        // Does nothing
    }
};

現(xiàn)在其障,mBinder是 Stub類的一個實例(一個 Binder)银室,用于定義服務(wù)的 RPC 接口。 在下一步中励翼,將向客戶端公開該實例蜈敢,以便客戶端能與服務(wù)進行交互。
在實現(xiàn) AIDL 接口時應注意遵守以下這幾個規(guī)則:

  • 由于不能保證在主線程上執(zhí)行傳入調(diào)用汽抚,因此您一開始就需要做好多線程處理準備抓狭,并將您的服務(wù)正確地編譯為線程安全服務(wù)。
  • 默認情況下造烁,RPC 調(diào)用是同步調(diào)用否过。如果您明知服務(wù)完成請求的時間不止幾毫秒,就不應該從 Activity 的主線程調(diào)用服務(wù)惭蟋,因為這樣做可能會使應用掛起(Android 可能會顯示“Application is Not Responding”對話框)— 您通常應該從客戶端內(nèi)的單獨線程調(diào)用服務(wù)苗桂。
  • 您引發(fā)的任何異常都不會回傳給調(diào)用方。

** 3.向客戶端公開該接口**
您為服務(wù)實現(xiàn)該接口后告组,就需要向客戶端公開該接口煤伟,以便客戶端進行綁定。 要為您的服務(wù)公開該接口惹谐,請擴展 Service并實現(xiàn) onBind()持偏,以返回一個類實例,這個類實現(xiàn)了生成的 Stub(見前文所述)氨肌。以下是一個向客戶端公開 IRemoteService
示例接口的服務(wù)示例鸿秆。

public class RemoteService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }
    @Override
    public IBinder onBind(Intent intent) {
        // Return the interface
        return mBinder;
    }
    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        public int getPid(){
            return Process.myPid();
        }
        public void basicTypes(int anInt, long aLong, boolean aBoolean,
            float aFloat, double aDouble, String aString) {
            // Does nothing
        }
    };
}

現(xiàn)在,當客戶端(如 Activity)調(diào)用 bindService() 以連接此服務(wù)時怎囚,客戶端的 onServiceConnected()回調(diào)會接收服務(wù)的 onBind()方法返回的 mBinder實例卿叽。
客戶端還必須具有對 interface 類的訪問權(quán)限桥胞,因此如果客戶端和服務(wù)在不同的應用內(nèi),則客戶端的應用 src/目錄內(nèi)必須包含 .aidl文件(它生成 android.os.Binder接口 — 為客戶端提供對 AIDL 方法的訪問權(quán)限)的副本考婴。
當客戶端在 onServiceConnected()回調(diào)中收到 IBinder時贩虾,它必須調(diào)用 YourServiceInterface.Stub.asInterface(service)
以將返回的參數(shù)轉(zhuǎn)換成 YourServiceInterface
類型。例如:

IRemoteService mIRemoteService;private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Following the example above for an AIDL interface,
        // this gets an instance of the IRemoteInterface, which we can use to call on the service
        mIRemoteService = IRemoteService.Stub.asInterface(service);
    }
    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "Service has unexpectedly disconnected");
        mIRemoteService = null;
    }
};

如需查看更多示例代碼沥阱,請參見 ApiDemos 中的 RemoteService.java
類缎罢。

通過 IPC 傳遞對象


通過 IPC 接口把某個類從一個進程發(fā)送到另一個進程是可以實現(xiàn)的。 不過考杉,您必須確保該類的代碼對 IPC 通道的另一端可用策精,并且該類必須支持Parcelable 接口。支持 Parcelable接口很重要崇棠,因為 Android 系統(tǒng)可通過它將對象分解成可編組到各進程的原語咽袜。
如需創(chuàng)建支持 Parcelable協(xié)議的類,您必須執(zhí)行以下操作:

  • 讓您的類實現(xiàn) Parcelable接口枕稀。
  • 實現(xiàn) writeToParcel询刹,它會獲取對象的當前狀態(tài)并將其寫入 Parcel。
  • 為您的類添加一個名為 CREATOR的靜態(tài)字段萎坷,這個字段是一個實現(xiàn) Parcelable.Creator接口的對象凹联。
  • 最后,創(chuàng)建一個聲明可打包類的 .aidl文件(按照下文 Rect.aidl文件所示步驟)食铐。如果您使用的是自定義編譯進程匕垫,切勿**在您的編譯中添加 .aidl文件。 此 .aidl文件與 C 語言中的頭文件類似虐呻,并未編譯象泵。

AIDL 在它生成的代碼中使用這些方法和字段將您的對象編組和取消編組。
例如斟叼,以下這個 Rect.aidl文件可創(chuàng)建一個可打包的 Rect類:

package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;

以下示例展示了 Rect類如何實現(xiàn) Parcelable協(xié)議偶惠。

import android.os.Parcel;
import android.os.Parcelable;
public final class Rect implements Parcelable {
    public int left;
    public int top;
    public int right;
    public int bottom;
    public static final Parcelable.Creator<Rect> CREATOR = newParcelable.Creator<Rect>() {
        public Rect createFromParcel(Parcel in) {
            return new Rect(in);
        }
        public Rect[] newArray(int size) {
            return new Rect[size];
        }
    };
    public Rect() {
    }
    private Rect(Parcel in) {
        readFromParcel(in);
    }
    public void writeToParcel(Parcel out) {
        out.writeInt(left);
        out.writeInt(top);
        out.writeInt(right);
        out.writeInt(bottom);
    }
    public void readFromParcel(Parcel in) {
        left = in.readInt();
        top = in.readInt();
        right = in.readInt();
        bottom = in.readInt();
    }
}

Rect
類中的編組相當簡單±噬看一看 Parcel上的其他方法忽孽,了解您可以向 Parcel 寫入哪些其他類型的值。

警告:別忘記從其他進程接收數(shù)據(jù)的安全影響谢床。 在本例中兄一,Rect從 Parcel讀取四個數(shù)字,但要由您來確保無論調(diào)用方目的為何這些數(shù)字都在相應的可接受值范圍內(nèi)识腿。

調(diào)用 IPC 方法


調(diào)用類必須執(zhí)行以下步驟出革,才能調(diào)用使用 AIDL 定義的遠程接口:

  1. 在項目 src/目錄中加入 .aidl文件。
  2. 聲明一個 IBinder 接口實例(基于 AIDL 生成)渡讼。
  3. 實現(xiàn) ServiceConnection骂束。
  4. 調(diào)用 Context.bindService()耳璧,以傳入您的 ServiceConnection實現(xiàn)。
  5. 在您的 onServiceConnected()實現(xiàn)中展箱,您將收到一個 IBinder實例(名為 service)旨枯。調(diào)用YourInterfaceName.Stub.asInterface((IBinder)service)
    ,以將返回的參數(shù)轉(zhuǎn)換為 YourInterface 類型混驰。
  6. 調(diào)用您在接口上定義的方法攀隔。您應該始終捕獲 DeadObjectException異常,它們是在連接中斷時引發(fā)的栖榨;這將是遠程方法引發(fā)的唯一異常竞慢。
  7. 如需斷開連接,請使用您的接口實例調(diào)用 Context.unbindService()治泥。

有關(guān)調(diào)用 IPC 服務(wù)的幾點說明:

  • 對象是跨進程計數(shù)的引用。
  • 您可以將匿名對象作為方法參數(shù)發(fā)送遮精。

如需了解有關(guān)綁定到服務(wù)的詳細信息居夹,請閱讀綁定服務(wù)文檔。
以下這些示例代碼摘自 ApiDemos 項目的遠程服務(wù)示例代碼本冲,展示了如何調(diào)用 AIDL 創(chuàng)建的服務(wù)准脂。

public static class Binding extends Activity {
    /**
 The primary interface we will be calling on the service.
 */
    IRemoteService mService = null;
    /**
 Another interface we use on the service.
 */
    ISecondary mSecondaryService = null;
    Button mKillButton;
    TextView mCallbackText;
    private boolean mIsBound;
    /**
     * Standard initialization of this activity.  Set up the UI, then wait
     * for the user to poke it before doing anything.
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.remote_service_binding);
        // Watch for button clicks.
        Button button = (Button)findViewById(R.id.bind);
        button.setOnClickListener(mBindListener);
        button = (Button)findViewById(R.id.unbind);
        button.setOnClickListener(mUnbindListener);
        mKillButton = (Button)findViewById(R.id.kill);        mKillButton.setOnClickListener(mKillListener);
        mKillButton.setEnabled(false);
        mCallbackText = (TextView)findViewById(R.id.callback);
        mCallbackText.setText("Not attached.");
    }
    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            mService = IRemoteService.Stub.asInterface(service);
            mKillButton.setEnabled(true);
            mCallbackText.setText("Attached.");
            // We want to monitor the service for as long as we are
            // connected to it.
            try {
                mService.registerCallback(mCallback);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even
                // do anything with it; we can count on soon being
                // disconnected (and then reconnected if it can be restarted)
                // so there is no need to do anything here.
            }
            // As part of the sample, tell the user what happened.
            Toast.makeText(Binding.this, R.string.remote_service_connected,
                    Toast.LENGTH_SHORT).show();
        }
        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mKillButton.setEnabled(false);
            mCallbackText.setText("Disconnected.");
            // As part of the sample, tell the user what happened.            Toast.makeText(Binding.this, R.string.remote_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };
    /**
     * Class for interacting with the secondary interface of the service.
     */
    private ServiceConnection mSecondaryConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // Connecting to a secondary interface is the same as any
            // other interface.
            mSecondaryService = ISecondary.Stub.asInterface(service);
            mKillButton.setEnabled(true);
        }
        public void onServiceDisconnected(ComponentName className) {
            mSecondaryService = null;
            mKillButton.setEnabled(false);
        }
    };
    private OnClickListener mBindListener = new OnClickListener() {
        public void onClick(View v) {
            // Establish a couple connections with the service, binding
            // by interface names.  This allows other applications to be
            // installed that replace the remote service by implementing
            // the same interface.
            Intent intent = new Intent(Binding.this, RemoteService.class);
            intent.setAction(IRemoteService.class.getName());
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
            intent.setAction(ISecondary.class.getName());
            bindService(intent, mSecondaryConnection, Context.BIND_AUTO_CREATE);
            mIsBound = true;
            mCallbackText.setText("Binding.");
        }
    };
    private OnClickListener mUnbindListener = new OnClickListener() {
        public void onClick(View v) {
            if (mIsBound) {
                // If we have received the service, and hence registered with
                // it, then now is the time to unregister.
                if (mService != null) {
                    try {
                        mService.unregisterCallback(mCallback);
                    } catch (RemoteException e) {
                        // There is nothing special we need to do if the service
                        // has crashed.
                    }
                }
                // Detach our existing connection.
                unbindService(mConnection);
                unbindService(mSecondaryConnection);
                mKillButton.setEnabled(false);
                mIsBound = false;
                mCallbackText.setText("Unbinding.");
            }
        }
    };
    private OnClickListener mKillListener = new OnClickListener() {
        public void onClick(View v) {
            // To kill the process hosting our service, we need to know its
            // PID.  Conveniently our service has a call that will return
            // to us that information.
            if (mSecondaryService != null) {
                try {
                    int pid = mSecondaryService.getPid();
                    // Note that, though this API allows us to request to
                    // kill any process based on its PID, the kernel will
                    // still impose standard restrictions on which PIDs you
                    // are actually able to kill.  Typically this means only
                    // the process running your application and any additional
                    // processes created by that app as shown here; packages
                    // sharing a common UID will also be able to kill each
                    // other's processes.
                    Process.killProcess(pid);
                    mCallbackText.setText("Killed service process.");
                } catch (RemoteException ex) {
                    // Recover gracefully from the process hosting the
                    // server dying.
                    // Just for purposes of the sample, put up a notification.
                    Toast.makeText(Binding.this,                            R.string.remote_call_failed,
                            Toast.LENGTH_SHORT).show();
                }
            }
        }
    };
    // ----------------------------------------------------------------------    // Code showing how to deal with callbacks.
    // ----------------------------------------------------------------------
    /**
     * This implementation is used to receive callbacks from the remote
     * service.
     */
    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
        /**
         * This is called by the remote service regularly to tell us about
         * new values.  Note that IPC calls are dispatched through a thread
         * pool running in each process, so the code executing here will
         * NOT be running in our main thread like most other things -- so,
         * to update the UI, we need to use a Handler to hop over there.
         */
        public void valueChanged(int value) {            mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
        }
    };
    private static final int BUMP_MSG = 1;
    private Handler mHandler = new Handler() {
        @Override
 public void handleMessage(Message msg) {
            switch (msg.what) {
                case BUMP_MSG:
                    mCallbackText.setText("Received from service: " + msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市檬洞,隨后出現(xiàn)的幾起案子狸膏,更是在濱河造成了極大的恐慌,老刑警劉巖添怔,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件湾戳,死亡現(xiàn)場離奇詭異,居然都是意外死亡广料,警方通過查閱死者的電腦和手機砾脑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艾杏,“玉大人韧衣,你說我怎么就攤上這事」荷#” “怎么了畅铭?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長勃蜘。 經(jīng)常有香客問我硕噩,道長,這世上最難降的妖魔是什么元旬? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任榴徐,我火速辦了婚禮守问,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坑资。我一直安慰自己耗帕,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布袱贮。 她就那樣靜靜地躺著仿便,像睡著了一般。 火紅的嫁衣襯著肌膚如雪攒巍。 梳的紋絲不亂的頭發(fā)上嗽仪,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音柒莉,去河邊找鬼闻坚。 笑死,一個胖子當著我的面吹牛兢孝,可吹牛的內(nèi)容都是我干的窿凤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼跨蟹,長吁一口氣:“原來是場噩夢啊……” “哼雳殊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起窗轩,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤夯秃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痢艺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仓洼,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年堤舒,在試婚紗的時候發(fā)現(xiàn)自己被綠了衬潦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡植酥,死狀恐怖镀岛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情友驮,我是刑警寧澤漂羊,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站卸留,受9級特大地震影響走越,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜耻瑟,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一旨指、第九天 我趴在偏房一處隱蔽的房頂上張望赏酥。 院中可真熱鬧,春花似錦谆构、人聲如沸裸扶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呵晨。三九已至,卻和暖如春熬尺,著一層夾襖步出監(jiān)牢的瞬間摸屠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工粱哼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留季二,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓揭措,卻偏偏與公主長得像戒傻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蜂筹,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 綁定服務(wù): 綁定服務(wù)是客戶端-服務(wù)器接口中的服務(wù)器。綁定服務(wù)可讓組件(例如 Activity)綁定到服務(wù)芦倒、發(fā)送請求...
    pifoo閱讀 1,229評論 0 4
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理艺挪,服務(wù)發(fā)現(xiàn),斷路器兵扬,智...
    卡卡羅2017閱讀 134,660評論 18 139
  • Android跨進程通信IPC整體內(nèi)容如下 1麻裳、Android跨進程通信IPC之1——Linux基礎(chǔ)2、Andro...
    隔壁老李頭閱讀 10,762評論 13 43
  • 近期有不少小伙伴問信達會計器钟,有沒有工程會計方面的資料津坑,今天會計培訓班特地幫大家整理以下工程做賬流程,希望對大家有幫...
    下雨天_西夕閱讀 746評論 0 1
  • 終于一步步踱出來傲霸, 肆無忌憚的雨聲已不再 一滴滴 打濕了堅硬的石灰地疆瑰, 濺起浪漫的雨花,紅男綠女 臉頰上再多表情也...
    小狗汪汪汪閱讀 156評論 0 1