这个问题从一开始接触到Android开发就困扰我很久了,平常除错少用中文log这个问题影响到不大,但是碰到需要把data (通常是远端的json or 本地端cache的sqlite)印出来观察这一种除错的情境时,这问题就头大了!
问了google也没有好解答,在android的google code里issue 1590就是在陈述这个问题,下面Comment提供的方法我试不出来,有趣的是用adb logcat在console下是不会有乱码的,所以问题一定出在ADT上,最近自已build了ADT trunk来用,刚好又遇到需要dump中文的data来debug的case,所以就尝试着自已来trace问题。
LogCat的相关的code都在LogPanel.java里,
public class LogPanel extends SelectionDependentPanel { private LogCatOuputReceiver mCurrentLogCat; @Override protected Control createControl(Composite parent) {...} private TabItem createTab(LogFilter filter, int index, boolean fillTable) {...} /** * Sent when a new device is selected. The new device can be accessed * with {@link #getCurrentDevice()}. */ @Override public void deviceSelected() { startLogCat(getCurrentDevice()); } public void startLogCat(final IDevice device) { if (device == mCurrentLoggedDevice) { return; } // if we have a logcat already running if (mCurrentLoggedDevice != null) { stopLogCat(false); mCurrentLoggedDevice = null; } resetUI(false); if (device != null) { // create a new output receiver mCurrentLogCat = new LogCatOuputReceiver(); // start the logcat in a different thread new Thread("Logcat") { //$NON-NLS-1$ @Override public void run() { while (device.isOnline() == false && mCurrentLogCat != null && mCurrentLogCat.isCancelled == false) { try { sleep(2000); } catch (InterruptedException e) { return; } } if (mCurrentLogCat == null || mCurrentLogCat.isCancelled) { // logcat was stopped/cancelled before the device became ready. return; } try { mCurrentLoggedDevice = device; device.executeShellCommand("logcat -v long", mCurrentLogCat, 0 /*timeout*/); //$NON-NLS-1$ } catch (Exception e) { Log.e("Logcat", e); } finally { // at this point the command is terminated. mCurrentLogCat = null; mCurrentLoggedDevice = null; } } }.start(); } } }
class Device的method executeShellCommand有三个参数分别是String command, IShellOutputReceiver receiver, int maxTimeToOutputResponse,它会透过class AdbHelper来做一下adb utility的指令操作。在第xx行时,执行了logcat -v long并把输出丢给LogCatOutputReceiver处理,所以我们的重点在于class LogCatOutputReceiver。继续追进去method executeShellCommand会发现它会把所有接收的data丢给interface IShellOutputReceiver的method addOutput处理,现在这个角色就是class LogCatOutputReceiver,而它的method addOutput是继承class MultiLineReceiver而来,问题就出在这里,把ISO -8859-1改成UTF-8就可以了...
public abstract class MultiLineReceiver implements IShellOutputReceiver { /* (non-Javadoc) * @see com.android.ddmlib.adb.IShellOutputReceiver#addOutput( * byte[], int, int) */ public final void addOutput(byte[] data, int offset, int length) { if (isCancelled() == false) { String s = null; try { s = new String(data, offset, length, "ISO-8859-1"); //问题在这里,把所有输出的字串都使用ISO-8859-1 decode } catch (UnsupportedEncodingException e) { // normal encoding didn't work, try the default one s = new String(data, offset,length); } // ok we've got a string if (s != null) { // if we had an unfinished line we add it. if (mUnfinishedLine != null) { s = mUnfinishedLine + s; mUnfinishedLine = null; } // now we split the lines mArray.clear(); int start = 0; do { int index = s.indexOf("\r\n", start); //$NON-NLS-1$ // if \r\n was not found, this is an unfinished line // and we store it to be processed for the next packet if (index == -1) { mUnfinishedLine = s.substring(start); break; } // so we found a \r\n; // extract the line String line = s.substring(start, index); if (mTrimLines) { line = line.trim(); } mArray.add(line); // move start to after the \r\n we found start = index + 2; } while (true); if (mArray.size() > 0) { // at this point we've split all the lines. // make the array String[] lines = mArray.toArray(new String[mArray.size()]); // send it for final processing processNewLines(lines); } } } } }
然后重新编译DDMS即可。
$javac -classpath ./ddms.jar:./ddmlib.jar MultiLineReceiver.java
下面便为重新编译后的的ADT,其中除了中文问题,其他与官方的完全相同。