Galgo是Android日誌類庫,用於在屏幕上顯示應用的日誌信息。這對於測試人員和開發人員很是有用,能夠根據屏幕上的日誌文件瞭解應用出現BUG時發生的事情。 java
能夠定義屏幕上顯示日誌的背景顏色、文本顏色、文本大小和日誌顯示的行數。 android
https://github.com/inaka/galgo
git
public class ExampleActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_example); // add some customization to the log messages GalgoOptions options = new GalgoOptions.Builder() .numberOfLines(15) .backgroundColor(Color.parseColor("#D9d6d6d6")) .textColor(Color.BLACK) .textSize(15) .build(); Galgo.enable(this, options); Galgo.log("I am a log message"); } public void onDestroy() { super.onDestroy(); // always call disable to avoid memory leaks Galgo.disable(this); } }
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.inaka.galgo"> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <application android:allowBackup="true" android:label="@string/app_name"> <service android:name=".GalgoService" /> </application> </manifest>
改造了一下代碼, github
1.把Service作成一個全局的, 而不是bind到惟一的Activity , 這纔是全局的Log express
2.把GalgoOptions 放到了Galgo.java中 apache
/* * Copyright (C) 2014 Inaka. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @author Henrique Boregio (henrique@inakanetworks.com) */ package com.kookong.tv.debug; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import com.hzy.tvmao.TmApp; public class Galgo { private static final Handler UI_HANDLER = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { if (!isServiceRunning) {//service 中止了就不要往service發消息了 return; } String message = (String) msg.obj; Intent intent = new Intent(TmApp.getContext(), GalgoService.class); intent.putExtra(MESSAGE, message); TmApp.getContext().startService(intent); } }; public static final String MESSAGE = "galgo.message"; public static final String ARG_OPTIONS = "galgo.options"; private static final String TAG = "Galgo"; private static GalgoOptions sOptions; private static Context sContext; private static boolean isServiceRunning; /** * * Starts a new Galgo with custom {@link com.inaka.galgo.GalgoOptions} * * @param context * Context * @param options * Custom {@link com.inaka.galgo.GalgoOptions} */ public static void enable(GalgoOptions options) { sOptions = options; init(); } /** * Starts a new Galgo with default {@link com.inaka.galgo.GalgoOptions} * * @param context * Context */ public static void enable() { enable(new GalgoOptions.Builder().build()); } private static void init() { sContext = TmApp.getContext(); checkPermission(sContext); // start a new service with our options Intent intent = new Intent(sContext, GalgoService.class); intent.putExtra(ARG_OPTIONS, sOptions); TmApp.getContext().startService(intent); isServiceRunning = true; } public static void disable() { Intent intent = new Intent(sContext, GalgoService.class); sContext.stopService(intent); sContext = null; isServiceRunning = false; } /** * Logs a String message to the screen. This String will be overlayed on top * of the UI elements currently displayed on screen. As a side effect, this * message will also be logged to the standard output via * {@link android.util.Log}. * * @param message * String to be displayed */ public static void log(String message) { Log.i(TAG, message); Message msg = UI_HANDLER.obtainMessage(0, message); msg.sendToTarget(); } private static void checkPermission(Context context) { String permission = "android.permission.SYSTEM_ALERT_WINDOW"; int status = context.checkCallingOrSelfPermission(permission); if (status == PackageManager.PERMISSION_DENIED) { throw new IllegalStateException("in order to use Galgo, " + "please add the permission " + permission + " to your AndroidManifest.xml"); } } public static final class GalgoOptions implements Parcelable { public final int numberOfLines; public final int backgroundColor; public final int textColor; public final int textSize; /** * Contains options for Galgo. Defines * * @param builder */ private GalgoOptions(Builder builder) { numberOfLines = builder.numberOfLines; backgroundColor = builder.backgroundColor; textColor = builder.textColor; textSize = builder.textSize; } /** * Builder for {@link com.inaka.galgo.GalgoOptions} */ public static class Builder { private int numberOfLines = 10; private int backgroundColor = 0xD993d2b9; private int textColor = 0xFFFFFFFF; private int textSize = 10; /** * * @param n * number of lines * @return */ public Builder numberOfLines(int n) { ensurePositiveInt(n, "number of lines must be > 0"); numberOfLines = n; return this; } /** * Sets the background color of the log messages * * @param color * @return */ public Builder backgroundColor(int color) { backgroundColor = color; return this; } /** * Sets the text color of the log messages * * @param color * @return */ public Builder textColor(int color) { textColor = color; return this; } /** * Sets the text size of the messages * * @param size * @return */ public Builder textSize(int size) { ensurePositiveInt(size, "text size must be > 0"); textSize = size; return this; } /** * Creates a {@link com.inaka.galgo.GalgoOptions} with the * customized parameters * * @return */ public GalgoOptions build() { return new GalgoOptions(this); } } private static void ensurePositiveInt(int value, String msg) { if (value <= 0) { throw new IllegalArgumentException(msg); } } // Parcelable implementation private GalgoOptions(Parcel source) { numberOfLines = source.readInt(); backgroundColor = source.readInt(); textColor = source.readInt(); textSize = source.readInt(); } public static final Creator<GalgoOptions> CREATOR = new Creator<GalgoOptions>() { @Override public GalgoOptions createFromParcel(Parcel source) { return new GalgoOptions(source); } @Override public GalgoOptions[] newArray(int size) { return new GalgoOptions[size]; } }; @Override public int describeContents() { return 0; // No special content. } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(numberOfLines); dest.writeInt(backgroundColor); dest.writeInt(textColor); dest.writeInt(textSize); } } }
/* * Copyright (C) 2014 Inaka. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @author Henrique Boregio (henrique@inakanetworks.com) */ package com.kookong.tv.debug; import java.util.ArrayDeque; import java.util.Collection; import java.util.Queue; import android.app.Service; import android.content.Intent; import android.graphics.PixelFormat; import android.os.Binder; import android.os.IBinder; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.BackgroundColorSpan; import android.view.Gravity; import android.view.WindowManager; import android.widget.TextView; import com.hzy.tvmao.utils.LogUtil; import com.hzy.tvmao.utils.SystemUtil; import com.kookong.tv.debug.Galgo.GalgoOptions; public class GalgoService extends Service { private TextView mTextView; private GalgoOptions mOptions; private final Queue<String> mLines = new ArrayDeque<>(); @Override public int onStartCommand(Intent intent, int flags, int startId) { LogUtil.d("onStartCommand"); //首次建立有options GalgoOptions options = intent.getExtras().getParcelable(Galgo.ARG_OPTIONS); if (options != null) { mOptions = options; } //顯示log的時候有Message String message = intent.getExtras().getString(Galgo.MESSAGE); if (!TextUtils.isEmpty(message)) { displayText(message); } return super.onStartCommand(intent, flags, startId); } @Override public void onCreate() { super.onCreate(); mTextView = new TextView(this); WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); wm.addView(mTextView, params); } public void displayText(String text) { mLines.add(text); if (mLines.size() > mOptions.numberOfLines) { mLines.poll(); } redraw(mLines); } private void redraw(Collection<String> texts) { mTextView.setTextSize(mOptions.textSize); mTextView.setTextColor(mOptions.textColor); mTextView.setPadding(SystemUtil.getScreenWH()[0] / 4, 0, 0, 0); Spannable spannable = new SpannableString(TextUtils.join("\n", texts)); spannable.setSpan(new BackgroundColorSpan(mOptions.backgroundColor), 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); mTextView.setText(spannable); } @Override public void onDestroy() { super.onDestroy(); if (mTextView != null) { WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); wm.removeView(mTextView); } } @Override public IBinder onBind(Intent intent) { return null; } }
另外一個開源項目, 顯示全部日誌 app
https://github.com/jgilfelt/GhostLog less